English: tycss-reference.en.md
本文档是 TyControls v1 样式语言(.tycss)的权威语法参考,面向主题作者与控件开发者。
文中每一条语法规则均与引擎实现(source/tyControls.Css.Lexer.pas、Css.Parser.pas、
Css.Values.pas、StyleModel.pas、Painter.pas)逐条核对,如与其他文档冲突,以本文为准。
加载入口:TTyStyleModel.LoadFromFile(文件名) / LoadFromCss(源码字符串)。
语法错误抛出 ETyCssError(含行/列号);属性值非法(如无法解析的颜色)在加载时抛出异常;
未知的属性名被静默忽略,不会报错。
库内置一个基础层(由 themes/light.tycss 生成),始终垫在已加载主题下面。它不是名为
default 的主题:default 是 themes/auto.tycss,和每套内置皮肤一样,自己写一部分键,
其余交给基础层。
ResolveStyle 怎样合并两层,取决于规则从哪里来:
- 主题(
ThemeName、ThemeFile、LoadTheme、LoadThemeCss)给某 typeKey 写了普通 规则(不带类、不带状态),内置层对这个键就整体失效,完全由主题决定(包括"留空"的属性 也不会从内置层渗漏)。主题没写普通规则的键整键回退到内置层。因此完整主题与未引入本特性 前渲染一致,而部分主题不会导致未覆盖控件不可见。 - 追加的补丁(
LoadThemeCssAdditive、控制器的StyleOverride)不接管任何键,只改它 写的属性,不管这个键来自主题还是内置层(§8.1)。
升级既有主题的人先读 §8.1。 v3 把 51 个"借用别人 typeKey"的控件拆成了自己的键; 由于覆盖语义是按 typeKey 全有全无,你的主题没提过的新键会整键回落到内置 light 值。
.tycss 是一个刻意精简的 CSS 方言:
- 顶层只有两种结构:
:root变量块 和 样式规则; - 选择器只有一种形态:
类型 [.变体] [:状态],可用逗号并列多个; - 不支持后代选择器、子选择器、通配符等任何组合选择器;
- 变量在使用处惰性求值,支持
var(--x)与裸--x两种写法; - 颜色支持
rgb/rgba构造函数,以及lighten/darken/alpha/mix调整函数,可任意嵌套。
下面是一个可直接使用的最小完整主题:
/* my-theme.tycss —— 最小可用主题 */
:root {
--accent: #3B82F6;
--surface: #FFFFFF;
--on-surface: #1F2937;
--border: #D1D5DB;
--danger: #EF4444;
--radius: 6px;
--focus-ring: var(--accent); /* 焦点环颜色;可独立覆盖 */
}
TyButton {
background: var(--surface);
color: var(--on-surface);
border-color: var(--border);
border-width: 1px;
border-radius: var(--radius);
padding: 6px;
font-size: 10px;
font-weight: 400;
}
TyButton:hover { background: darken(--surface, 4%); }
TyButton:focus { border-color: var(--accent); outline: 2px var(--focus-ring); }
TyButton:active { background: darken(--surface, 10%); }
TyButton:disabled { opacity: 0.5; }
TyButton.primary { background: var(--accent); color: #FFFFFF; border-color: var(--accent); }
TyButton.primary:hover { background: lighten(--accent, 8%); }
TyButton.primary:active { background: darken(--accent, 8%); }
TyLabel {
background: alpha(#FFFFFF, 0); /* 完全透明 */
color: var(--on-surface);
font-size: 10px;
}
TyEdit, TyComboBox {
background: var(--surface);
color: var(--on-surface);
border-color: var(--border);
border-width: 1px;
border-radius: var(--radius);
padding: 4px;
font-size: 10px;
}
TyEdit:focus, TyComboBox:focus { border-color: var(--accent); outline: 2px var(--focus-ring); }
TyPanel {
background: var(--surface);
border-color: var(--border);
border-width: 1px;
border-radius: var(--radius);
padding: 8px;
}更完整的实例见仓库内置主题:themes/light.tycss、themes/dark.tycss、themes/builtin/showcase.tycss。
一个 .tycss 文件是若干 :root 块与样式规则的序列,顺序任意、可交错:
:root { --a: #fff; }
TyButton { background: var(--a); }
:root { --a: #000; } /* 允许多个 :root 块;同名变量后定义者覆盖 */只支持块注释 /* ... */,可跨行。不支持 // 行注释。
| Token | 形式 | 说明 |
|---|---|---|
| 颜色字面量 | #rgb #rrggbb #rrggbbaa |
# 后只吃十六进制字符;长度必须为 3、6 或 8,否则求值时报错 |
| 数字 | 10 0.45 .5 |
无符号;可带小数。词法上数字不带负号(见下方"负数") |
| 标识符 | bold --accent -2px |
首字符 [a-zA-Z_-],后续可含数字;- 可作首字符,故 --accent、-2px 都是单个标识符 |
| 函数名 | var( lighten( url( |
标识符紧跟 ((中间不能有空格)即为函数调用开头 |
| 字符串 | "Noto Sans" 'a' |
单/双引号;无转义序列;不能含同种引号 |
| 单位/符号 | px % deg |
px、deg 是普通标识符,紧跟数字时被粘合为 6px、90deg;% 是独立符号,紧贴前一数字 |
| 标点 | : ; { } . , ( ) |
负数:-2px 在词法上是一个标识符 token,但长度/数值求值函数能正确解析它,
因此 shadow: 0px -2px 4px #0000002E; 这类负偏移是合法的。
- 每条声明形如
属性名: 值;,分号必不可少,包括块内最后一条; - 值一直读到分号 /
}为止;括号内的逗号、冒号不会截断值; - 类型名、变体名、伪类名、属性名、函数名的匹配均不区分大小写 (习惯上请按本文与内置主题的写法书写);
- 同一规则块内重复写同一属性,后写的覆盖先写的。
:root {
--accent: #3B82F6;
--radius: 6px;
--hover-bg: lighten(--accent, 8%); /* 变量可以引用其他变量 */
}- 变量名必须以
--开头,否则解析报错; - 值是原始文本,在使用处才求值(惰性求值)。因此变量可以前向/相互引用其他变量, 也可以存放颜色函数、渐变参数片段等任何表达式;
- 同名变量重复定义时,后定义者覆盖(包括跨多个
:root块); - 引用未定义的变量在使用该变量的规则被求值时(即加载样式表时)抛出异常。
颜色、长度、数值三类表达式中,以下两种写法完全等价:
TyButton {
background: var(--accent); /* CSS 标准写法 */
border-color: --accent; /* 裸变量写法,同样合法 */
border-radius: var(--radius);
padding: --radius;
}在函数参数里同样两种都可用,内置主题大量使用裸写法以求简洁:
TyButton:hover { background: lighten(--accent, 8%); } /* 裸 --x */
TyButton:focus { border-color: darken(var(--border), 10%); } /* var(--x) */注意:var(...) 形式只能整体作为一个值或一个函数参数使用,不存在字符串拼接。
除 --focus-ring(§5.14)外,内置主题在 :root 中还约定了若干语义令牌,供子部件
typeKey 与文本类控件引用。它们都是普通 --变量,可被主题作者整体覆盖:
| 令牌 | 内置取值(light) | 语义 / 用途 |
|---|---|---|
--selection |
alpha(var(--accent), 0.30) |
文本选区高亮带底色(accent 着色的半透明);由 TyTextSelection 引用,使 Edit/Memo 的选区与列表选中行视觉同源 |
--muted |
alpha(var(--on-surface), 0.5) |
弱化前景色(占位符 / 提示文字);由 TyTextHint 引用 |
--overlay-hover |
alpha(var(--on-surface), 0.12) |
半透明悬停叠加层(如页签关闭 × 的圆角底片);由 TyTabClose 引用 |
--input-bg |
var(--surface) |
输入类控件的底色:编辑框、下拉框、列表框、备忘框、微调框、日历、日期框、树、列表视图 |
--input-border |
var(--border) |
同一批输入类控件的边框色;与 --border 分开,皮肤改输入框边框时不会连带分隔线、网格线和面板边框 |
--grid-bg |
var(--surface) |
数据表格正文的底色 |
:root {
--selection: alpha(var(--accent), 0.30);
--muted: alpha(var(--on-surface), 0.5);
--overlay-hover: alpha(var(--on-surface), 0.12);
}改这些令牌即可全局调整对应的外观,无需逐个控件改写。皮肤没有专属规则的控件由内置基础层绘制, 皮肤把
--input-bg、--input-border、--grid-bg和--radius系列指向自己编辑框的取值, 这些控件就会和皮肤自己的编辑框一致。这些令牌与上面的--accent、--on-surface一样 在使用处惰性求值,可引用其他变量。
:root 里还有一批令牌是给控件读的。大多是尺寸,定内边距、间距和槽位大小,比如
--listview-text-margin、--treeview-margin,值可以写 6px、6 或 var(--x);也有取值是
词或颜色的,比如树线的 --treeview-lines: classic、--treeview-lines-color: #a0a0a0(§3.5)。
整个应用:写在主题里,或写进控制器的 StyleOverride:
:root {
--listview-text-margin: 6px;
--treeview-margin: 6px;
}只改一个控件:写进这个控件自己的 StyleOverride,只对它生效,优先于主题:
--treeview-margin: 10px; --treeview-icon-gap: 6px;
控件 StyleOverride 里的 -- 声明也是同一块里其他声明的变量,比如
--w: 2px; border-width: var(--w);。值算不出来(写错了单位、引用了没定义的变量)时,
这一条不起作用,沿用主题的值。取值是词的令牌,值如果只是一个引用(var(--x) 或 --x),
读到的是它指向的值;指不到东西时同样沿用主题的值。
目前按控件读令牌的是:TTyListView(含 TTyShellListView)读的全部尺寸令牌,即
--listview-*、--row-height 和 --scrollbar-size;TTyTreeView(含 TTyShellTreeView)的
--treeview-margin、--treeview-icon-gap 和 §3.5 的四个树线令牌;TTyMemo 的 --memo-tab-size;
TTyPopover 的六个 --popover-* 令牌(它不是控件,但 StyleOverride 的规则相同)。
其他控件暂时只认主题里的值。另外,每个控件都认 §3.6 的 --over-parent-chrome。
3.0.4 以前的版本会忽略控件 StyleOverride 里的 -- 声明,用新版本保存的窗体在旧版本里照样能打开。
树线和展开符号怎么画,由下面四个令牌决定。和 §3.4 的令牌一样,可以写在主题里,也可以只写进
某一棵树的 StyleOverride。
| 令牌 | 取值 | 默认 | 含义 |
|---|---|---|---|
--treeview-lines |
guides / classic / none |
guides |
guides:在每个祖先节点的展开符号下面画一条竖线,贯穿它后代的各行;节点自己这一层不画,所以线碰不到展开符号。classic:完整连线,包括节点自己那段从上一行下来、再拐向内容的线。none:不画。 |
--treeview-lines-style |
solid / dotted / dashed / 一到两个长度 |
solid |
线型。dotted 就是 1px 1px,dashed 就是 3px 3px。写长度时同 SVG 的 stroke-dasharray:先实后空,只写一个就是实空等长,比如长虚线写 6px 3px。单位是逻辑像素,随 DPI 缩放。 |
--treeview-lines-color |
颜色 | 树的边框色 | 线的颜色,可以用 var() 和颜色函数。 |
--treeview-expander |
chevron / plus-minus |
chevron |
plus-minus 是带方框的 +/-,画在连线交汇的地方,classic 连线到框边为止。点击区域和 chevron 一样。 |
内置主题里,classic 和 xp 两个皮肤用 plus-minus、classic、dotted,其余都用默认值。
点线和虚线的图案以树的内容区原点为准,不是每一段线各自从头开始:相邻几行的竖线连成一条, 拐角处横竖两条线落在同一套图案上,滚动时图案跟着内容走。
属性 ShowTreeLines 仍是总开关:设成 False 就不画线,令牌怎么写都一样。有没有展开符号
仍由 ShowButtons 决定。
/* 整个应用都用经典的树 */
:root {
--treeview-expander: plus-minus;
--treeview-lines: classic;
--treeview-lines-style: dotted;
}只让某一棵树不画线,在它的 StyleOverride 里写:
--treeview-lines: none;
3.0.4 以前没有这些令牌,树画的是实线的完整连线加 chevron。
子控件放到容器的边框、圆角或分隔线上时,容器会在子控件画完之后把这几样补画在上面,线不会在子控件那里断开。 会这样做的容器:
| 容器 | 补画的东西 |
|---|---|
TTyPanel,以及 TTyExPanel、TTyPaintPanel、TTyScrollBox、TTyScrollPanel、TTyControlBar、TTyCoolBar、TTyGridPanel、TTyRelativePanel |
边框、焦点环、圆角外的父背景 |
TTyCard |
同上,加标题下和操作栏上的分隔线 |
TTyGroupBox,以及 TTyRadioGroup、TTyCheckGroup、TTyToolGroupPanel |
边框、焦点环、圆角外的父背景,加标题和它在上边框断开的那一段 |
TTyPageControl |
页面区的边框、焦点环、圆角外的父背景(以前当前页会盖住其中三条边) |
TTyToolBar |
底部细线 |
只有看得见的边框或焦点环才算外框:border-width: 0 又没有焦点环的面板,圆角本来就看不出来,
里面子控件的直角照旧。滚动框里贴边的滚动条自己就会画宿主的外框,宿主不再重复画。
不想让某个子控件被盖住,在它的 StyleOverride 里写:
--over-parent-chrome: 1;
写在主题或控制器 StyleOverride 的 :root 里,整个应用都不再补画。值是 0、false、none
或者不写,都表示照常补画。代码里也可以整个关掉:TyChromeOverlayEnabled := False;
(tyControls.Base 单元,默认 True)。
补画只发生在 TyControls 自己的控件上;原生 LCL 控件放上去,照旧会盖住外框。这个行为从 3.0.5 开始。
选择器 ::= 类型名 [ "." 变体名 ] [ ":" 状态名 ]
选择器列表 ::= 选择器 { "," 选择器 }
- 类型名:控件的 typeKey(见第 8 节),如
TyButton; - 变体名(可选):任意标识符,对应控件
StyleClass属性中的某个 token,如.primary; 每个选择器最多一个变体; - 状态名(可选):只能是
hover、active、focus、disabled、selected五个之一 (checked是selected的别名,解析成同一个状态),其他伪类名直接解析报错。 每个选择器最多一个状态 —— 不能链式书写:selected:disabled。
TyButton { ... } /* 类型基础样式 */
TyButton.primary { ... } /* 变体 */
TyButton:hover { ... } /* 状态 */
TyButton.primary:hover { ... } /* 变体 + 状态 */
TyEdit:focus, TyComboBox:focus { ... } /* 逗号列表:同一组声明注册到多个选择器 */以下写法都会导致解析错误或不被识别,引擎没有任何组合选择器:
- 后代 / 子选择器:
TyPanel TyButton、TyPanel > TyButton - 通配符:
* - 多变体:
TyButton.primary.large(一个选择器只允许一个.变体) - 纯类 / 纯状态选择器:
.primary、:hover(必须以类型名开头) - 属性选择器、
!important、@media一概不支持 (@import与@mode是支持的 at-规则,只是它们不是选择器;@import必须出现在 所有:root与规则之前)
控件每次重绘时计算当前状态集:
disabled(控件Enabled = False)是排他的:禁用时只有 disabled 一个状态, hover / focus / active 均被忽略;hover:鼠标位于控件上;active:鼠标左键按下未释放(对 CheckBox 等也是"按住"而非"选中");focus:控件拥有键盘焦点。基于TTyGraphicControl的控件(TTyLabel一族、TTyProgressBar、仪表族等)没有 focus 状态;能取得焦点的窗口化控件都支持它;selected:条目/单元格/页签一类可选中的部件处于选中态。它由子部件的解析点给出 (如TyListViewItem、TyCalendarCell、TyGridCell、TySegmentedItem),而不是控件整体的瞬时状态。
多个状态可同时成立(如鼠标悬停在已聚焦的按钮上 = hover + focus)。
一个键支持哪些状态,取决于它的解析点传了什么 —— 第 8 节逐键列出内置主题实际写过的伪类,
并点名那些解析时传空状态集、因而伪类选择器是死的键(如 TyLinkLabelLink)。
控件最终样式由多层 TTyStyleSet 按固定顺序合并,后层只覆盖其显式声明过的属性:
- 类型基础层:
Type { }; - 变体层:控件
StyleClass中的每个空格分隔 token,按其在StyleClass字符串中的文本顺序依次应用Type.变体 { }(StyleClass可含多个变体,如"primary large"); - 状态层:对当前成立的每个状态,按固定顺序
selected → hover → focus → active → disabled 依次处理;
每个状态内先应用
Type:状态 { },再按变体顺序应用Type.变体:状态 { }。selected排在最前是有意的:它是静态的驻留层,让 hover/active 能逐属性盖掉它冲突的部分, 而 selected 独有的属性(例如只有它声明的border-color)仍然留下来。
由此可见:带状态的规则永远晚于(强于)不带状态的规则,带变体的状态规则永远晚于 同状态的纯类型规则。
TyButton { background: #FFFFFF; }
TyButton:hover { background: #F0F0F0; } /* 状态层 */
TyButton.primary { background: var(--accent); } /* 变体层 */鼠标悬停在一个 StyleClass = primary 的按钮上时,合并顺序为:
TyButton → TyButton.primary → TyButton:hover。
状态层在变体层之后,所以 TyButton:hover 的 #F0F0F0 会盖掉 .primary
的强调色——primary 按钮一悬停就"褪色"。正确做法是为变体补一条同状态规则,
它在 TyButton:hover 之后应用:
TyButton.primary:hover { background: lighten(--accent, 8%); }引擎对每个 (类型, 变体, 状态) 组合正序应用所有命中的规则,后一条按属性覆盖前一条
—— 与浏览器 CSS 一致,也是 @import / 追加加载能逐属性叠加的原因:
TyButton { background: #FF0000; color: #111111; }
TyButton { background: #00FF00; } /* 只覆盖 background;color 仍是 #111111 */这条行为由 tests/test.StyleModel.pas 的 TestDuplicateRuleLastWins 守住。
即便如此,仍不建议在同一个文件里重复定义同一选择器 —— 需要并列声明时把它们写进同一个规则块,
读起来才不用满文件找覆盖。
第三方控件覆写 GetStyleTypeKey 报自己的键(比如 TagButton)时,可以在 Pascal 里登记它从哪个键来
(写法见 subclassing.md 第 2 节):
TyRegisterTypeKeyParent('TagButton', 'TyButton'); // 在 tyControls.StyleModel 里登记之后,TagButton 先拿到 TyButton 的全部样式(内置层、主题层、变体、状态,和解析 TyButton
本身一模一样),再用自己的规则逐属性盖上去。主题里没写 TagButton 时,它和普通按钮完全一样;
写了就只需写不同的地方:
TagButton { border-radius: var(--radius-pill); }子键的规则按 §4.4 的阶段和父键交错:普通规则 → 变体 → 各状态(selected → hover → focus → active → disabled),每个阶段里先父后子。所以:
- 子键的规则盖过父键同一阶段或更早阶段写的值:
TagButton { }盖过TyButton { },TagButton.primary盖过TyButton.primary,TagButton:hover盖过TyButton:hover; - 父键更晚阶段写的值仍然有效:
TyButton:hover、TyButton:disabled盖过TagButton { }和TagButton.primary,TyButton:disabled也盖过TagButton:hover。
TagButton { background: #DCFCE7; } /* 平时是绿的 */
/* 悬停时用 TyButton:hover 的底色 */
TagButton:hover { background: #BBF7D0; } /* 想要自己的悬停色,就写这一条 */这和浏览器里「带状态、带类的选择器优先」的直觉一致:给子键写一条普通背景,不会吃掉父键的悬停和禁用效果。
「父键写的值」指父键自己解析出来的结果。例如主题打开了 PropertyCascade、又写了一条普通的
TyButton { background },它在 TyButton 自己的结果里已经盖掉了内置的 :hover 底色,那么对
TagButton 来说这个背景也只算普通阶段写的。
链可以有好几级,比如 FancyTag → TagButton → TyButton:每个阶段里从最上面的父键往下叠,
同一阶段子键胜。一条链最多 8 个键。
链根以下的键,同一阶段里先内置层、后用户层。 链根(最上面的父键)照 §4.4 解析:先整个内置层
(各阶段),再整个用户层。链根下面的键则按阶段走,每个阶段里先内置层、再用户层。所以把一个内置键
登记成子键、又开着 PropertyCascade 时,它内置的 :hover 会盖过主题给它写的普通规则(阶段更晚);
链根自己不会这样(用户层整个排在内置层之后)。第三方键没有内置规则,看不出这条区别。
按属性组交错,不按单条声明。 「哪个阶段写了这个值」按属性组记,组的划分和控件的 StyleOverride 合并进来时一样:一组里任何一个声明都算写了整组,让位时整组一起让。平时一条声明就是一组,要留意的是这几组:
-
outline与outline-offset是一组(焦点环)。子键只写outline-offset,父键在更晚的阶段 (常见的是:focus)写了outline,那个阶段里整组用父键的,子键的偏移也一起让掉:TyButton:focus { outline: 2px var(--focus-ring); outline-offset: 1px; } TagButton { outline-offset: 6px; } /* 聚焦时不生效:整组用 TyButton:focus 的 */ TagButton:focus { outline-offset: 6px; } /* 写在同一阶段,子键胜 */
-
background-size、background-blur跟background一组:父键的:hover换了底色,子键的background-size也跟着让。 -
玻璃效果
glass-blur/glass-tint是单独一组,不跟background走:父键的:hover只换底色时, 子键的玻璃参数不受影响;父键的:hover写了glass-tint,悬停时子键的glass-blur也一起让掉。 -
border-radius的四个角是一组:让位时四角一起回到父键的值。 -
shadow的颜色、模糊、偏移是一组。
没有把 outline-offset 单独拆成一项:组的划分和 StyleOverride 合并是同一套,主题作者只记一套规则;
拆开要给样式集加一个新的属性标志,每个读 Present 的地方都得认它,而要碰到这种差别,得父子键在
不同阶段分写同一个焦点环的两半,把偏移和焦点环写在同一条规则里就不会遇到。
内置层的让位按键各算各的(§8.1)。主题写了 TagButton { }(无变体、无状态),只让 TagButton
自己的内置规则失效;TyButton 的内置规则照常垫在下面。第三方键在内置层本来就没有规则,
所以这条只在把内置键登记成子键时才看得出来。
子部件键要分别登记。 控件用 GetStyleTypeKey + 'Fill' 之类拼出来的子部件键不会自动跟着走:
TagMeter 的填充解析的是 TagMeterFill,想继承 TyMeterFill 就再登记一条。
登记规则:
- 在单元的
initialization里登记,finalization里用TyUnregisterTypeKeyParent撤销。登记表是 整个进程共用的;运行中途才登记,已经画好的控件不会自己重画。 - 在
initialization里抛异常会让程序还没启动就中止——比如两个包给同一个键登记了不同的父键, 后加载的那个一抛,整个程序起不来。包里登记用TyTryRegisterTypeKeyParent:出错时返回False、 登记表不变、不抛异常,控件只是不继承父键的规则;登记成功或同一对已经登记过返回True。 - 键名不分大小写,必须是标识符(字母或
_开头,后面是字母、数字、_、-)。 - 下列情况抛
ETyCssError,登记表保持原样:键名不合法;成环(包括把一个键登记成它自己的父键); 一条链超过 8 个键;同一个键已经登记了别的父键(同一对再登记一次不算错)。 - 查询用
TyTypeKeyParent、TyTypeKeyChain、TyGetRegisteredTypeKeys。
工具也认这条链:设计期 StyleClass 下拉会列出父键的变体(TagButton 也有 primary、danger、
ghost);控制器级 StyleOverride 编辑器的补全和右侧参考列表都列出登记过的键。lint 不检查 typeKey 名,
子键不会被报。
没登记父键的 typeKey 跟以前一样:只吃主题里为它写的规则。
引擎可识别的属性共 23 个(另有 background-color 作为 background 的别名),其余属性名一律被静默忽略。
本节展开其中通用的 16 个。另外 7 个是窗框 / 材质专用,本手册尚未逐条展开
(render-style 见 §9 第 17 条;
其余 background-size、background-blur、glass-blur、glass-tint、
background-under-titlebar 目前只有源码 TyApplyDeclaration 为准)。
所有长度均为逻辑像素(96 DPI 基准),绘制时按控件实际 DPI 缩放。
长度值可写 6px 或裸 6(px 后缀可省略),也可用 var(--x) / 裸 --x。
background: <颜色表达式> ;
background: linear-gradient(<角度>deg, <色标1>, <色标2>[, …]) ;
- 纯色:任何颜色表达式(
#hex、var()、裸--x、颜色函数,见第 6 节); - 渐变:见第 7 节;角度必须是第一个参数,值以
linear-gradient(开头才按渐变解析;支持 2+ 多色标(可带位置)。
TyPanel { background: var(--surface); }
TyButton { background: linear-gradient(90deg, lighten(--accent, 10%), var(--accent)); }background-color 是 background 的别名(仅纯色语义,不接受渐变),二者效果相同:
TyEdit { background-color: rgb(255, 255, 255); } /* 等价于 background: rgb(255, 255, 255); */background-image: url(<路径>) slice(<上> <右> <下> <左>) [repeat] ;
url()与slice()都必须出现,缺一报错;slice的四个值为整数像素,顺序固定:上 右 下 左(同 CSS 四值顺序), 表示源图四边不拉伸的边框宽度;- 末尾可选
repeat(v3):四条边与中心区平铺(1:1 重复)而非拉伸——用于带花纹/ 纹理边的位图皮肤;省略时为拉伸(与旧行为一致)。四角始终 1:1,不受影响; - 路径可加引号也可不加,但路径中绝对不能含空格:词法器会在未加引号的路径中插入
空格(如
panel.png变为panel. png),引擎通过"无条件删除所有空格"来复原, 因此真实空格也会被一并删除而悄悄读错。目录名、文件名都请保持无空格; - 相对路径相对于进程当前工作目录解析(没有"相对主题文件"的解析);文件不存在时 该填充被静默跳过(不报错、不绘制);
- 不支持
var()。
TyPanel { background-image: url(assets/panel.png) slice(8 8 8 8); }color: <颜色表达式> ;
用于文字以及 tier-b 单色字形(CheckBox 勾选符、ComboBox 下拉箭头、SpinEdit/ScrollBar 箭头、 Tab 关闭 × 等,见 §8.7)。
TyButton.primary { color: #FFFFFF; }注:滑块/旋钮等 tier-a 着色面不再借用本控件的 color,而由各自的子部件 typeKey
(TyScrollThumb、TyToggleKnob 等)的 background 决定——详见 §8.2 / §8.7。轨道/轨条背景仍来自父控件 background。
border-color: <颜色表达式> ;
border-width: <长度> ;
border-style: none | solid | outset | inset ;
三者配合:border-width 为 0(或未声明 border-color)时不描边。
solid 边框沿圆角矩形内侧描绘。
border-style 取值,默认 solid:
none— 强制不描边,即便border-width > 0;solid— 单色描边(沿圆角内侧);outset/inset(v3)— 两色 3D 立体斜角:上/左与下/右分别用较亮/较暗的边色,outset凸起、inset凹陷。亮/暗两色从border-color自动派生(lighten 上左 / darken 下右)。 斜角天然是方角(不套用圆角半径),适合经典风格控件(配border-radius: 0)。
不支持 dashed、dotted、groove、ridge 等其它样式。树线的点线、虚线另有令牌
--treeview-lines-style,见 §3.5。
TyEdit { border-color: var(--border); border-width: 1px; }
TyEdit:focus { border-color: var(--accent); }
TyPanel { border-style: none; } /* 显式去掉边框,即便有 border-width */
/* 经典凸起按钮:面色作为 border-color,自动派生高亮/阴影边 */
TyButton.classic { border: 2px outset #C0C0C0; border-radius: 0; background: #C0C0C0; }border: <宽度> [<样式>] <颜色> ;
一次性设置 border-width、border-style、border-color 三者,完全等价于分别书写它们。
顺序为宽度在前、颜色在后,中间可选写 solid / none / outset / inset 样式(省略时为 solid)。
颜色可用任意颜色表达式(#hex、var(--x)、rgb(...) / rgba(...),见第 6 节):
TyButton { border: 2px solid var(--accent); } /* width=2px, style=solid, color=accent */
TyEdit { border: 1px var(--border); } /* 省略样式,等价于 solid */
TyPanel { border: 0px none var(--border); } /* style=none → 强制不描边 */border-radius: <长度> ;
border-radius: <TL> <TR> <BR> <BL> ;
同时作用于背景填充、边框与焦点环形状。0px 为直角。
- 1 个值:四角使用相同半径(向后兼容原有行为);
- 4 个值:按 CSS 标准顺序依次为 左上、右上、右下、左下(
TL TR BR BL), 空格分隔。值可为长度或var(--x)/ 裸--x,支持0表示直角。
TyButton { border-radius: var(--radius); } /* 四角相同 */
TyTitleBar { border-radius: 6px 6px 0 0; } /* 上圆下方(标题栏 / 页签) */
TyTabControl { border-radius: var(--radius) var(--radius) 0 0; }内置主题对标题栏(TyTitleBar)与页签(TyTab)使用 var(--radius) var(--radius) 0 0
实现顶部圆角、底部直角效果。页签的圆角按标签在上方来写;TabPosition 为下方或两侧时,
圆角跟着转到标签伸出去的那一侧。
不支持:每角长手写法(border-top-left-radius 等)、百分比半径、
椭圆双半径写法(<a> / <b>)。
padding: <全部> ;
padding: <上下> <左右> ;
padding: <上> <左右> <下> ;
padding: <上> <右> <下> <左> ;
空格分隔,1 到 4 个值,语义与 CSS 一致;其它个数报错。
TyButton { padding: 6px; }
TyPanel { padding: 8px 12px; }
TyLabel { padding: 2px 6px 4px; }
TyEdit { padding: 4px 8px 4px 8px; }font-family: <字体名> ;
值按原始文本直接作为字体名。不要加引号:带引号书写时引号字符会被保留进字体名, 导致找不到字体。多词字体名直接裸写即可(空格会被规范为单个空格):
TyLabel { font-family: Noto Sans CJK SC; } /* 正确 */
/* font-family: "Noto Sans CJK SC"; 错误:引号会成为名字的一部分 */font-size: <数值>[px] ;
数值按"磅"(pt)解释:绘制时换算为像素 round(n × 96 / 72) 再做 DPI 缩放。
允许写 px 后缀,但后缀只是被剥掉,数值仍按 pt 处理——font-size: 10px 实际是 10pt
(96 DPI 下约 13 像素)。内置主题沿用了这一写法。
TyButton { font-size: 10px; } /* = 10pt */font-weight: bold | normal | <数值> ;
bold= 700,normal= 400,也可直接写数字(如600);- 渲染时只有粗体/常规两档:数值 ≥ 600 绘制为粗体,否则常规,没有中间字重。
TyTitleBar { font-weight: 700; }opacity: <0..1 小数> ;
对整个控件的绘制结果(含文字)统一施加不透明度,常用于 disabled 态:
TyButton:disabled { opacity: 0.5; }opacity 经由通用框架绘制路径(DrawFrame)生效。v1.1 中 TyCheckBox 与
TyRadioButton 已通过修改渲染路径使 opacity 与 shadow 正常生效;
所有 typeKey(含 TyLabel)均已支持。
shadow: <X偏移> <Y偏移> <模糊半径> <颜色> ;
- 四段以空格分隔,顺序固定:X 偏移、Y 偏移、模糊半径(均为逻辑像素,偏移可为负)、颜色;
- 颜色必须是单个 token:
#hex(推荐带 alpha 的#rrggbbaa)、var(--x)或裸--x。 因为值按空格切分,不能使用含逗号的颜色函数(alpha(...)、mix(...)等); 需要半透明请直接写 hex-alpha; - 颜色 alpha 为 0 时不绘制阴影;
- 阴影同样走 DrawFrame 路径,对
TyCheckBox/TyRadioButton无效。
TyButton { shadow: 0px 2px 4px #0000002E; }
TyPanel { shadow: 0px 1px 3px var(--shadow-color); }outline: <宽度> <颜色> ;
outline-offset: <长度> ;
在控件内部绘制一圈焦点指示边框。因为 TyControls 控件完全自绘、绘制结果被裁剪到控件矩形内, 焦点环总是向内偏移而非向外扩张。
outline: <宽度> <颜色>:宽度在前(开头为数字)、颜色在后(颜色表达式)。 与border独立,不影响布局与padding。设置此属性会将tpOutline加入Present集;outline-offset: <长度>:焦点环距控件外边缘的内缩距离,默认0(贴着控件边缘)。outline-offset单独不设置tpOutline——必须同时有outline规则焦点环才会绘制;outline: 0可在特定规则里禁用焦点环(引擎只处理outline简写与outline-offset,outline-width属性不被识别、静默忽略);- 焦点环的圆角跟随
border-radius的四角设置(各角减去outline-offset后箍合,最小为 0)。
通常写在 :focus 规则内,且引用 --focus-ring 语义令牌:
TyButton:focus { outline: 2px var(--focus-ring); }
TyEdit:focus { border-color: var(--accent); outline: 2px var(--focus-ring); outline-offset: 2px; }这不是一个属性名,而是内置主题在 :root 中定义的语义变量:
:root {
--focus-ring: var(--accent);
}所有内置主题(light.tycss、dark.tycss 等)均在 :root 中定义 --focus-ring,
并在所有可聚焦控件的 :focus 规则中用 outline: 2px var(--focus-ring) 绘制焦点环。
主题作者可按以下方式定制:
| 目的 | 写法 |
|---|---|
| 改变焦点环颜色 | :root { --focus-ring: #FF8800; } |
| 全局禁用焦点环 | 各 :focus 规则中写 outline: 0; 或省略 outline |
| 单控件禁用焦点环 | TyEdit:focus { outline: 0; } |
| 回归直角焦点环 | 对应控件写 border-radius: 0; |
颜色表达式 = #hex | var(--x) | 裸 --x | rgb(...) / rgba(...) | 以下函数(参数本身又是颜色表达式,
可任意嵌套)。函数名不区分大小写。百分比参数的 % 后缀可写可不写
(8% 与 8 等价)——唯一例外是 alpha,见下。
任何接受颜色的位置(color、background / background-color、border-color、border: 简写、
颜色函数的参数等)都可使用上述全部形式。
向白色方向提亮:每通道 ch + (255 − ch) × p/100;alpha 保持不变。
TyButton.primary:hover { background: lighten(--accent, 8%); }向黑色方向压暗:每通道 ch × (1 − p/100);alpha 保持不变。
TyButton:active { background: darken(--surface, 10%); }把颜色的 alpha 替换为给定值(RGB 不变),0 全透明、1 不透明。不透明度也可以写成
百分比:alpha(#fff, 50%) 等于 alpha(#fff, 0.5)。
陷阱:不带
%的数按 0..1 理解,不是百分比。alpha(#fff, 50)会被钳成完全不透明, 要写0.5或50%。
TyCaptionButton:hover { background: alpha(#FFFFFF, 0.18); }
TyLabel { background: alpha(#FFFFFF, 0); } /* 透明背景 */线性混合,百分比是第二个颜色的占比:结果 = 颜色1 × (1−p/100) + 颜色2 × p/100。
RGB 与 alpha 四个通道都参与混合。
:root { --tint: mix(--surface, --accent, 20%); } /* 80% surface + 20% accent */按 RGB 分量构造颜色。r / g / b 为 0..255 的整数;rgba 的第四个参数 a 是
不透明度,既可写 0..1 的小数,也可写带 % 的百分比(0.6 与 60% 等价)。
rgb(...) 等价于 alpha 为 1 的 rgba(...)。可用在任何接受颜色的位置(含 border: 与 background)。
TyEdit { background-color: rgb(255, 255, 255); }
TyLabel { color: rgba(0, 0, 0, 0.6); } /* 60% 不透明的黑 */
TyButton { border: 2px solid rgb(59, 130, 246); }TyButton:hover {
background: lighten(mix(--surface, var(--accent), 15%), 4%);
}background: linear-gradient(<角度>deg, <色标1>, <色标2>[, …<色标N>]) ;
- 角度必须是第一个参数(angle-first),
deg后缀可省略,角度可带小数; - 其后是 2 个或更多颜色色标(v3 起支持多色标,用于光泽等效果);少于 2 个色标报错;
- 每个色标是
颜色 [位置]:颜色是完整的颜色表达式(可用函数、变量,内部逗号不会误切分), 位置是可选的百分比或0..1数值(如#fff 0%、var(--accent) 50%); - 位置按 CSS 规则归一化:首个缺省为
0、末个为1,中间缺省的在相邻已定位色标间均匀插值, 且强制不递减(更小的位置会被上钳到前一个); - 不支持
to right等关键字方向(方向仍由角度决定,见 7.1)。
多色标示例(经典玻璃光泽:顶部高亮带):
TyButton.primary {
background: linear-gradient(90deg, #FFFFFF 0%, #EAEAEA 48%, #DADADA 52%, #EDEDED 100%);
}渐变轴穿过控件中心,方向按屏幕坐标系(x 向右、y 向下)的数学角度计算
(dx = cos θ, dy = sin θ),起始色在角度反方向端点:
| 角度 | 方向(起始色 → 终止色) |
|---|---|
0deg |
左 → 右 |
90deg |
上 → 下 |
180deg |
右 → 左 |
270deg |
下 → 上 |
注意这与浏览器 CSS 约定(0deg 朝上、90deg 朝右)不一致。
内置 showcase 主题中常见的 linear-gradient(90deg, …) 是自上而下的渐变:
TyButton.primary {
background: linear-gradient(90deg, lighten(--accent, 10%), var(--accent));
/* 顶部较亮,底部为强调色 */
}渐变端点取在渐变轴与控件包围盒的交点上,渐变铺满整个控件矩形。
选择器中的类型名即控件 GetStyleTypeKey 返回的 typeKey(含子部件 typeKey)。
第三方控件自己的键可以登记一个父键、继承它的全部规则,见 §4.5。
本节是主题作者的权威键表。清单逐条取自 themes/light.tycss —— 该文件是唯一事实来源,
source/tyControls.DefaultTheme.pas(编译进库的内置基础层)由它生成并逐字节同步。
当前规模:
| 数量 | |
|---|---|
light.tycss 已定义的 typeKey |
183 |
| 控件解析、但基础层刻意不定义的键(§8.4) | 8 |
| 主题里有定义、但没有任何代码解析的死键(§8.6) | 3 |
v3 起的重大变化。 此前 51 个控件解析的是别的控件的 typeKey,主题层根本够不着它们 (commit
b824a49、d58eada)。这些控件现在各有自己的键,并作为并列选择器加进原捐赠者 的规则块 —— 所以内置主题下像素一动不动,但每一个都变成可单独换肤的钩子。 既有第三方.tycss必须补新选择器,原因见下一小节。
对主题,ResolveStyle 的默认行为(PropertyCascade 关闭时)是按 typeKey 全有全无:
- 主题对某 typeKey 没写普通规则 → 该键整体回落到内置 light 基础层。主题给这个键的类或
状态写的规则(
TyFoo.x、TyFoo:hover)叠在内置规则上面; - 主题对该 typeKey 写了普通规则(不带类、不带状态)→ 内置层对这个键整体失效,主题 没写的属性也不会从内置层渗漏。
判据是 TTyStyleModel.UserHasTypeKey,只看有没有这样一条规则,不看你覆盖了哪些属性。
追加的补丁不是主题。 LoadThemeCssAdditive 和控制器的 StyleOverride 不接管任何键。
主题交给内置层的键,先把内置层给它的规则复制进来,补丁再叠在上面,所以在 default 主题下
写 TyCascader { padding: 6px 13px; } 只改内距。这个键的 :hover、:focus 规则仍然盖在
补丁上面,和主题自己的一样。3.0.4 之前,这样的补丁会让内置层对该键失效,控件丢掉补丁没写的
所有属性。
这条规则正是升级时最容易踩的坑,也是本节最需要告诉你的一件事:
拆键 = 你的主题多了一批没写过的键。 从前
TyGauge { background: url(...) }一条规则同时管住十四个仪表控件;现在TTyMeter解析TyMeter,你的主题从没提过这个名字,于是TyMeter整键回落到 内置 light 值 —— 在一张图片皮肤上就是照片中间一块不透明的灰方块。 所以给TyGauge换过肤,就必须把TyMeter、TyRating、TyDial……一起点名。
内置主题用并列选择器处理这件事,第三方主题照抄这个写法即可:
/* 一条规则,十八个可分别覆盖的名字 */
TyGauge, TyMeter, TyLevelMeter, TyDial, TyGearDial, TyAnalogClock, TyCircularProgress,
TyActivityIndicator, TyActivityBar, TyGearActivityIndicator, TySparkline, TyRating,
TyLColorPicker, TyHSColorPicker, TyMeterTick, TyAnalogClockHand, TyGearDialTeeth, TyColorArea {
background: var(--surface-sunk);
border-color: var(--border);
}排查手段:tests/test.themes.pas 里的 alias guard 会把每个新键与它拆自的捐赠键逐条比对,
themes/ 与 examples/theming/ 下所有样式表都在其覆盖范围内。自制主题可照它的做法自查。
- 画什么 —— 该键在控件上负责哪一块像素。写「文字色」意味着该键只被读
color, 写background意味着只被读背景,不写则表示整套盒模型属性都生效。 - 谁解析 —— 解析这个键的控件类。一个键可能被多个控件解析(共用是有意的,见 §8.5)。
- 状态 / 变体 —— 内置 light 主题实际写过的伪类与
.变体。 未列出不等于控件不上报该状态:任何键都可以写:hover/:active/:focus/:disabled/:selected, 只要控件在重绘时上报了对应状态就会命中(§4.3)。反过来,少数键的解析点传的是空状态集 (如TyLinkLabelLink),那么它的伪类选择器是死的 —— 表里会点名。
| typeKey | 画什么 | 谁解析 | 状态 / 变体 |
|---|---|---|---|
TyForm |
窗体背景;经 ApplyChromeTheme 写进窗体 Color。内置 background: var(--form-bg),比内容表面略深。窗口圆角 border-radius、阴影 window-shadow 见 ttyform.md §8;macOS 上窗口圆角由系统决定,border-radius 不起作用,window-shadow 照样生效 |
TTyForm |
— |
TyFormSurface |
刻意不定义(§8.4) | TTyFormSurface |
— |
TyTitleBar |
自绘标题栏条带(与 TyRibbonQuickAccess 共用一个规则块) |
TTyTitleBar |
— |
TyCaptionButton |
标题栏系统按钮 | TTyCaptionButton |
:hover :active;变体 .close(:hover/:active)、.min(:hover)、.max(:hover) |
下面六个键共用同一个规则块,取值逐字节相同,但可分别覆盖:
| typeKey | 画什么 | 谁解析 |
|---|---|---|
TyButton |
普通按钮外框 + 文字 | TTyButton(及同族未重写键的控件:TTyGlyphButton、TTyColorButton、TTyDropDownButton、TTyMenuButton、TTyTransferArrowButton) |
TySpeedButton |
工具条上的快捷按钮 | TTySpeedButton |
TyGlyphContainerButton |
带图标容器的按钮 | TTyGlyphContainerButton |
TyRibbonAppMenu |
Ribbon 左上角的应用菜单按钮 | TTyRibbonAppMenu |
TyButtonGroup |
分段按钮组的整体外框 | TTyButtonGroup |
TyUpDown |
上下微调器外框、中缝与箭头墨色 | TTyUpDown |
状态::hover :focus :active :disabled。
变体:.primary(:hover/:active)、.danger(:hover/:active)、
.ghost(平时透明,:hover/:active/:selected/:focus/:disabled 齐全)。
同族但独立成块的还有一个:
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyBadge |
按钮右上角的数字角标胶囊 | TTyBadge、TTyButton |
无状态 |
下面七个键共用同一个规则块(透明底 + --on-surface 墨色 + 基准字号):
| typeKey | 画什么 | 谁解析 |
|---|---|---|
TyLabel |
静态标题文字 | TTyLabel |
TyHtmlLabel |
富文本块(逐 run 绘制、下划线/删除线带) | TTyHtmlLabel |
TyLinkLabel |
超链接标签的盒子(字体/内容,不含链接墨色) | TTyLinkLabel |
TyShadowLabel |
带投影的文字 | TTyShadowLabel |
TyGlowLabel |
带光晕的文字 | TTyGlowLabel |
TyDivider |
分隔线及其嵌入标题 | TTyDivider |
TyCharImage |
字形/图标字符块 | TTyCharImage |
状态:仅 :disabled(opacity)。
| typeKey | 画什么 | 谁解析 | 注意 |
|---|---|---|---|
TyLinkLabelLink |
链接墨色,只读 color |
TTyLinkLabel |
解析时传空状态集,TyLinkLabelLink:hover / :disabled 是死选择器;悬停色由代码 TyLighten(col, 15) 合成 |
TTyHtmlLabel里<a>的墨色不经任何 typeKey ——RenderTo直接ResolveOverride('color: var(--accent);'),钉死在--accent。想改只能改--accent。TyHtmlLabelLink是推迟项,不存在(§8.4)。
| typeKey | 画什么 | 谁解析 | 状态 / 变体 |
|---|---|---|---|
TyEdit |
单行输入框 | TTyEdit 及同族未重写键的控件(TTyMaskEdit/TTyCurrencyEdit/TTyURLEdit/TTyNumericEdit/TTyCalcEdit/TTyValueEdit/TTyComboEdit/TTyTrackEdit);TTyCalculator 的显示条也显式解析它 |
:hover :focus :disabled |
TySpinEdit |
数字微调输入框 | TTySpinEdit |
:hover :focus :disabled |
TyMemo |
多行文本框 | TTyMemo |
:hover :focus :disabled |
TyComboBox |
下拉框字段(下拉箭头用 color) |
TTyComboBox 及同族的另外 11 个控件;TTyTreeSelect 刻意共用(§8.5) |
:hover :focus :disabled |
TyCascader |
级联选择字段 | TTyCascader |
:hover :focus :disabled |
TyDateTimePicker |
日期时间字段 | TTyDateTimePicker |
:hover :focus :disabled |
TyTextSelection |
文本选区高亮带,只读 background |
TTyEdit / TTyMemo / TTyDateTimePicker |
无状态 |
TyTextHint |
占位符 / 提示文字,只读 color |
TTyEdit / TTyTreeSelect |
无状态 |
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyCheckBox |
复选框的小方块(控件整体透明) | TTyCheckBox、TTyCheckListBox、TTyDateTimePicker |
:hover :active :focus :disabled |
TyRadioButton |
单选钮的圆圈 | TTyRadioButton |
:hover :active :focus :disabled |
TyToggleSwitch |
开关的轨道(Checked=True 时追加 :active) |
TTyToggleSwitch |
:hover :active :focus :disabled |
TyToggleKnob |
开关旋钮(tier-a 着色面) | TTyToggleSwitch |
无状态 |
TyTrackBar |
滑杆轨道 | TTyTrackBar |
:focus :disabled |
TyRangeTrackBar / TyMultiTrackBar / TyGradientTrackBar |
多滑块控件本体,父键 TyTrackBar(没写自己的规则时与 TTyTrackBar 一样) |
TTyRangeTrackBar / TTyMultiTrackBar / TTyGradientTrackBar |
:focus :disabled |
TyTrackThumb |
滑杆滑块(tier-a);:focus 的 outline 画在滑块内侧(outline-offset 往里缩),默认主题用 --on-accent 画一圈环。多滑块控件会在滑块自己的类后面再加两个变体:.removing(拖出轨道、松手就删的滑块;控件把它的 opacity 乘到这个滑块的颜色上,默认主题取 --disabled-opacity)、.stop(TTyGradientTrackBar 的色标;用色标自己的颜色填充,默认主题加 1px --on-surface 的边)。写了自己 TyTrackThumb 规则的主题要连这两个变体一起写(§8.1) |
TTyTrackBar、TTyRangeTrackBar、TTyMultiTrackBar、TTyGradientTrackBar、TTyTrackEdit |
:hover :active :focus(控件有焦点时方向键要动的那个滑块) |
TyTrackSelection |
滑杆选区(SelStart..SelEnd)和 TTyRangeTrackBar 的区间,读 background、border-radius |
TTyTrackBar、TTyRangeTrackBar |
无 |
TyTrackSegment |
TTyMultiTrackBar 两个滑块之间的一段,按 Segments 项的 StyleClass 解析,读 background、border-radius。不带类时不画;默认主题有 .success .warning .danger 三个变体 |
TTyMultiTrackBar |
无 |
TyProgressBar |
进度条轨道 | TTyProgressBar |
:disabled |
TyProgressFill |
进度条填充段(tier-a);部分填充时只圆起始角 | TTyProgressBar |
无状态 |
下面十七个键共用同一个规则块(表面色 + 1px 边框 + --radius + --pad-container)。
TyPanel 之外的每一个,从前都只能通过改 TyPanel 来影响 —— 也就是会牵动全应用的容器:
| typeKey | 画什么 | 谁解析 |
|---|---|---|
TyPanel |
通用容器面板 | TTyPanel;TTyRelativePanel / TTyPaintPanel 刻意共用(§8.5) |
TyScrollBox |
滚动井(约定上比面板下沉) | TTyScrollBox;TTyScrollPanel 继承 |
TyScrollContent |
滚动容器的视口(裁剪内容的那个窗口化子控件)。只画 background,不画边框——框归外面的滚动框画。这个键必须有值且必须不透明:控件除了这块底色什么都不画,解析不到就露出宿主未主题化的擦除色(渐变底皮肤如 aero 在暗色模式下就是一块亮斑) |
TTyScrollContent(TTyScrollBox / TTyScrollPanel 的视口) |
TyExPanel |
可折叠卡片的外框(其 padding.right 同时决定标题右内缩) |
TTyExPanel |
TyChart |
图表外框、标题、图例、刻度标签、两轴与网格线 | TTyChart |
TyCalculator |
计算器背板(显示条另解析 TyEdit,按键是真的 TTyButton) |
TTyCalculator |
TyBevel |
凹凸装饰线(只画高光/阴影两条轨,不画面) | TTyBevel |
TySizeBox |
右下角尺寸夹点 | TTySizeBox |
TyControlBar |
可停靠工具条容器与其装饰性握把 | TTyControlBar |
TyCoolBar |
可拖拽分带工具条(握把可交互) | TTyCoolBar |
TyColorGrid |
色块矩阵与选中环 | TTyColorGrid |
TyShape |
矢量图元(椭圆/矩形/三角/线) | TTyShape |
TyStarShape |
星形图元 | TTyStarShape |
TyArrow |
箭头图元 | TTyArrow |
TyImageView |
缩放/平移图片背后的信箱底衬 | TTyImageView |
TyImage |
图片框(Transparent=False 时才画框) |
TTyImage |
TyPreviewBox |
文件对话框的预览井 + 空态提示文字 | TTyPreviewBox |
TyListGroupPanel |
导航手风琴的侧栏底面 | TTyListGroupPanel |
该块无状态规则。想让图片 :disabled 变淡,得自己写
TyImage:disabled { opacity: 0.5; } —— 内置主题没有为这一族声明任何状态规则。
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyExPanelHeader |
折叠面板的标题带:箭头 + 标题墨色与字体;background 是选择性的(写了才填带底,不写则透出面板表面) |
TTyExPanel |
:hover(带内悬停,内置未写,写了即生效) |
TyGroupBox |
分组框(必须声明 background,用于遮盖标题处的边框线) |
TTyGroupBox、TTyRadioGroup、TTyCheckGroup;TTyOfficeListBox/TTyOfficeComboBox 的分组标题带也解析它 |
— |
TyToolGroupPanel |
Ribbon 风格的命令簇(与 TyGroupBox 共块) |
TTyToolGroupPanel |
— |
TyGridPanel |
刻意不定义(§8.4) | TTyGridPanel |
— |
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyScrollBar |
滚动条轨道背景;端部箭头墨色用 color(tier-b) |
TTyScrollBar |
:hover :active :focus :disabled |
TyScrollThumb |
滑块的独立填充与圆角(tier-a) | TTyScrollBar |
:hover :active |
TySplitter |
分隔条(内置 background: none,只有握把墨色) |
TTySplitter |
:hover |
TyStatusBar |
状态栏条带 | TTyStatusBar |
— |
TyToolBar |
工具条条带 | TTyToolBar、TTyToolBarEx |
— |
TyToolSeparator |
工具条分隔线:border-color 画线、background 让它与条带无缝(与 TyToolBar 共块) |
TTyToolSeparator |
— |
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyListBox |
列表外框 | TTyListBox 及同族 14 个未重写键的控件;TTyComboBox/TTyValueListEditor 的下拉体、TTyPopupSurface、TTyGalleryGrid(画廊弹出网格)也解析它 |
:hover :focus :disabled |
TyValueListEditor |
属性网格外框(与 TyListBox 共块) |
TTyValueListEditor |
:hover :focus :disabled |
TyRibbonGallery |
Ribbon 画廊的内嵌行外框(与 TyListBox 共块) |
TTyRibbonGallery |
:hover :focus :disabled |
TyListItem |
单行条目:background 决定行底、color 决定文字 |
TTyListBox;TTyRibbonGallery 的图块也仍解析它 |
:hover :active(=选中行) |
TyValueListEditorRow |
属性网格的一行(与 TyListItem 共块) |
TTyValueListEditor |
:hover :active |
TyValueListEditorGroup |
属性网格的标题行(TTyValueRow.Heading,属性表格的分类)平时的底色、文字色、字重;挂在 TyValueListEditorRow 下面 |
TTyValueListEditor |
—(悬停、选中时照 TyValueListEditorRow 画) |
TyValueListEditorKey / -Value / -Divider / -Expander 由代码解析但基础层刻意不定义,见 §8.4。
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyTreeView |
树外框(与 TyListView 共块) |
TTyTreeView;TTyShellTreeView、TTyTreeSelect 的下拉树继承 |
— |
TyListView |
列表视图外框 | TTyListView;TTyShellListView 继承 |
— |
TyTreeNode |
一个树节点行 | TTyTreeView |
:hover :selected :disabled |
TyListViewItem |
一个列表视图条目(与 TyTreeNode 共块) |
TTyListView |
:hover :selected :disabled |
TyTreeHeader |
树的列头带(画在自己框内,不描边) | TTyTreeView |
— |
TyListViewHeader |
列表视图的列头带 | TTyListView |
— |
TyListViewGroupHeader |
列表视图的分组带(从前与列头带同键,无法分开配) | TTyListView |
— |
TyHeaderControl |
独立表头条:它就是外框,故 border-radius/shadow 在这里有意义(与上面三个共块) |
TTyHeaderControl |
— |
TyTreeHeaderSection |
一个列头单元格 | TTyTreeView;TTyHeaderControl 仍共用(其专属 section 键是推迟项) |
:hover :selected |
TyListViewHeaderSection |
列表视图的列头单元格(与上共块) | TTyListView |
:hover :selected |
TyTreeCheckBox |
树节点勾选框 | TTyTreeView |
:active :selected :disabled |
TyListViewCheckBox |
列表视图勾选框(与上共块) | TTyListView |
:active :selected :disabled |
TyListViewLine / TyListViewMarquee 由代码解析但基础层刻意不定义,见 §8.4。
网格自成一整套键,不借用树/列表的键。基础层给全套定义,所以新皮肤一条网格规则不写也能正常显示。
TTyDrawGrid / TTyStringGrid 刻意共用 TyGrid(§8.5)。网格的每一次解析都会带上控件的
StyleClass,所以 .变体 在子部件键上也有效 —— 这一点与 ListView / ValueListEditor 不同。
| typeKey | 画什么 | 状态 |
|---|---|---|
TyGrid |
网格外框 | — |
TyGridCell |
正文单元格(内置 background: none,让表面透出) |
:hover :selected |
TyGridCellSelectedInactive |
失焦时的选区(HideSelectionWhenInactive) |
— |
TyGridActiveCell |
焦点格(整行选中模式下指示光标所在格) | — |
TyGridCellMarked |
选区盖在用户显式着色的格上时的半透明层 | — |
TyGridSelectionFrame |
选区外框 + 右下角填充柄(color = 柄的描边色) |
— |
TyGridCellAlt |
斑马纹隔行底色 | — |
TyGridFixed |
冻结行列区 | — |
TyGridIndicator |
行头槽 | — |
TyGridHeader |
列头带 | — |
TyGridHeaderSection |
一个列头单元格 | :hover :selected :active |
TyGridHeaderGroup |
分组表头带(横跨若干列的上层标题) | — |
TyGridFilterRow |
内嵌筛选行(底色跟表面走、边框跟输入框走,以示"这里能打字") | — |
TyGridGroupRow |
分组带。内置 background: none 是刻意的当前观感;想要 chrome 底色自行声明 |
— |
TyGridSummaryRow |
汇总/页脚带。刻意没有 border-color —— RenderFooter 不读,写了是死属性 |
— |
TyGridLine |
格线,只读 background;设成透明即可去掉格子 |
— |
TyGridCheckBox |
勾选框单元格 | :selected |
TyGridHyperlink |
超链接单元格墨色 | — |
TyGridCommentMark |
批注角标墨色 | — |
TyGridProgress |
进度条单元格轨道 | — |
TyGridProgressFill |
进度条单元格填充 | — |
TyGridRating |
评分单元格的实心星 | — |
TyGridRatingEmpty |
评分单元格的空心星 | — |
TyGridButton |
按钮单元格(自己的键,免得改网格按钮波及全库按钮) | :hover :active |
同名警告:
TyGridCell被两个不相干的东西认领。 除数据网格的正文格外,TTyGridPanel的布局格子TTyGridCell的GetStyleTypeKey也返回'TyGridCell'。后者的Paint什么都不画,所以今天没有可见冲突;但它并不是一个 单独可寻址的钩子 —— 你写给数据网格的TyGridCell规则名义上也罩着它。
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyPageControl |
多页控件的页容器外框 | TTyPageControl |
:hover :focus :disabled |
TyTabSheet |
页体:只有不透明表面填充,无边框,以免透出 OS 窗体背景 | TTyTabSheet |
— |
TyTabSet |
只有页签条、没有页体的标签条 | TTyTabSet |
:hover :focus :disabled |
TyTab |
单个页签头(:active 在此表示选中而非按下) |
页签条引擎(TTyPageControl / TTyTabSet);Ribbon 的页签头也解析它 |
:hover :active |
TyTabClose |
关闭 × 悬停时的圆角底片(× 笔画本身用 TyTab 的 color,tier-b) |
页签条引擎 | 无状态 |
TyTabSet因为HasPageBody = False而跳过DrawFrame,只画一条基线导轨, 所以它实际只消费border-color与border-width;写background/border-radius/shadow不会被画出来。专属的TyTabSetRail是推迟项,不存在(§8.4)。
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyMenuBar |
顶部应用菜单条 | TTyMenuBar |
— |
TyMenuView |
下拉/右键弹出体 | TTyMenuView |
— |
TyMenuPopup |
弹出宿主(与 TyMenuView 同值的镜像键) |
TTyMenuView |
— |
TyMenuItem |
一行菜单项 / 菜单条单元;基础态的 border-color 就是分隔线墨色 |
TTyMenuBar / TTyMenuView |
:hover :active :disabled |
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyCalendar |
日历外框 | TTyCalendar、TTyDateTimePicker 的下拉日历 |
— |
TyCalendarTitle |
顶部年月标题(粗体) | TTyCalendar |
:hover |
TyCalendarWeekday |
星期行表头(弱化墨色) | TTyCalendar |
— |
TyCalendarCell |
一个日期格 | TTyCalendar |
:hover :selected :disabled |
TyDateTimePicker |
见 §8.2.4 | TTyDateTimePicker |
:hover :focus :disabled |
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyRibbon |
命令带表面 | TTyRibbon;TTyRibbonPage 刻意共用(§8.5);TTyPopupSurface 的默认 StyleKey 也是它 |
— |
TyRibbonGroup |
带标题的命令分组框 | TTyRibbonGroup |
— |
TyRibbonBackstage |
后台视图的内容面(与 TyRibbon 共块) |
TTyRibbonBackstage |
— |
TyRibbonQuickAccess |
快速访问条(与 TyTitleBar 共块) |
TTyRibbonQuickAccess |
— |
TyRibbonAppMenu |
见 §8.2.2 | TTyRibbonAppMenu |
同按钮族 |
TyRibbonGallery |
见 §8.2.8 | TTyRibbonGallery |
:hover :focus :disabled |
两处仍在借用,是推迟不是设计。 后台视图的侧栏与命令行解析
ResolveStyle('TyButton', 'primary')(含:hover/:active),所以侧栏只能是主按钮色; 画廊的图块仍解析TyListItem、弹出网格仍解析TyListBox。TyRibbonBackstageSidebar/-Item/-Back/-Separator、TyRibbonGalleryItem/TyRibbonGalleryPopup都不存在(§8.4)。另:
TyRibbonTab这个名字从未被使用 —— Ribbon 的页签头解析的是TyTab。 早先文档把它列为"预留",现已无此保留;不要为它写规则。
从前十四个互不相干的控件全部硬返回 TyGauge、并且都解析同一个 TyGaugeFill,
于是星级评分、模拟钟秒针与进度环按构造是同一个颜色。现在各有其键,共用一个规则块。
表盘/底面(与 TyGauge 共块:下沉底 + 边框):
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TyGauge |
仪表底面(本族的定义者,不是借用者) | TTyGauge |
:disabled |
TyMeter |
仪表读数区与文字 | TTyMeter |
:disabled |
TyLevelMeter |
电平表轨道 | TTyLevelMeter |
:disabled |
TyDial |
旋钮底面 | TTyDial |
:disabled |
TyGearDial |
齿轮旋钮底面 | TTyGearDial |
:disabled |
TyAnalogClock |
表盘与刻度(color) |
TTyAnalogClock |
:disabled |
TyCircularProgress |
环形进度的轨道环 | TTyCircularProgress |
:disabled |
TyActivityIndicator |
忙碌指示器的轨道环 | TTyActivityIndicator |
:disabled |
TyActivityBar |
跑马灯进度条的轨道框 | TTyActivityBar |
:disabled |
TyGearActivityIndicator |
齿轮忙碌指示器挖空的孔(角色与仪表相反:这里画的是孔不是轨道) | TTyGearActivityIndicator |
:disabled |
TySparkline |
迷你折线图底面 | TTySparkline |
:disabled |
TyRating |
评分控件底面 | TTyRating |
:disabled |
TyLColorPicker |
亮度条外框与游标(条身是 HSV 生成像素,无子部件键) | TTyLColorPicker |
:disabled |
TyHSColorPicker |
色相/饱和度方块外框与十字游标 | TTyHSColorPicker |
:disabled |
TyMeterTick |
仪表刻度,只读 color |
TTyMeter |
空状态集 |
TyAnalogClockHand |
时针 + 分针,只读 color |
TTyAnalogClock |
空状态集 |
TyGearDialTeeth |
齿轮齿,只读 background |
TTyGearDial |
空状态集 |
TyColorArea |
颜色对话框的 HSV 方块 / 色相条的边框与游标 | TTyHSVSquare / TTyHueBar |
空状态集 |
点亮部分(与 TyGaugeFill 共块:background: var(--accent)):
| typeKey | 画什么 | 谁解析 |
|---|---|---|
TyGaugeFill |
仪表填充。唯一一个硬写字面量的子部件键(ResolveStyle('TyGaugeFill', …)),其余都是 GetStyleTypeKey + '后缀' |
TTyGauge |
TyMeterNeedle |
仪表指针 | TTyMeter |
TyLevelMeterFill |
电平条 | TTyLevelMeter |
TyLevelMeterPeak |
峰值标线 —— 给它一个对比色,从前它与它要标示的条同色 | TTyLevelMeter |
TyDialPointer |
旋钮指针 | TTyDial |
TyGearDialPointer |
齿轮旋钮指针 | TTyGearDial |
TyAnalogClockSecondHand |
秒针 + 中心轴 | TTyAnalogClock |
TyCircularProgressFill |
环形进度的进度弧 | TTyCircularProgress |
TyActivityIndicatorFill |
旋转弧 | TTyActivityIndicator |
TyActivityBarFill |
行进中的色块 | TTyActivityBar |
TyGearActivityIndicatorFill |
齿与盘体 | TTyGearActivityIndicator |
TySparklineFill |
折线 | TTySparkline |
TySparklineDot |
折线端点 | TTySparkline |
TyRatingStar |
星形 | TTyRating |
TyRatingStar是本族唯一带新状态轴的键:预览悬停时它是带:hover解析的 (Rating.pas),而内置主题刻意不写TyRatingStar:hover,好让像素不动; 皮肤补上这一条即可给预览上色。
:disabled { opacity }在本族大部分成员上是空转的 ——Opacity只在DrawFrame里推进画笔,而本族只有 ActivityBar、LevelMeter、Sparkline 与TTyGauge的线性样式 走DrawFrame。上表照实列出了主题里写了的:disabled规则,但其中多数今天渲染不出效果。
| typeKey | 画什么 | 谁解析 | 状态 / 变体 |
|---|---|---|---|
TyCard |
卡片表面(头/尾是画在其上的带) | TTyCard |
:hover :disabled |
TyCardHeader |
标题带:只有分隔线与标题墨色,无背景故透出卡片自身表面 | TTyCard |
— |
TyCardActions |
底部动作带的分隔线 | TTyCard |
— |
TyTag |
标签胶囊 | TTyTag |
:disabled;变体 .accent .danger |
TyTagClose |
标签的关闭 × | TTyTag |
:hover |
TyAlert |
内嵌提示条 | TTyAlert |
:disabled;变体 .info .success .warning .error(硬契约:AlertType 直接映射到这四个名字) |
TyAlertClose |
提示条的关闭 × | TTyAlert |
:hover |
TyNotification |
角落浮层通知 | TTyNotification |
:hover;变体 .info .success .warning .error(变体的 color 是图标墨色) |
TyNotificationClose |
通知的关闭 × | TTyNotification |
:hover |
TyEmpty |
空状态占位(透明,躺在空列表自己的表面上) | TTyEmpty |
:disabled |
TyEmptyImage |
空状态插图的墨色(比正文更淡,故单独成键) | TTyEmpty |
— |
TyHint |
主题化的 tooltip 表面(替代原生 LCL 提示) | TTyHintWindow、TTyBalloonHint |
— |
TyPopover |
可承载控件的浮层表面(箭头是从同一表面切出的,故无需单独的键) | TTyPopover |
— |
TyPopoverTitle |
浮层标题 | TTyPopover |
— |
TyChartTooltip |
图表内的悬停提示框。图表不画这个框除非这个键给了背景 | TTyChart |
— |
| typeKey | 画什么 | 谁解析 | 状态 |
|---|---|---|---|
TySegmented |
分段控件的凹槽 | TTySegmented |
:focus :disabled |
TySegmentedItem |
一个分段(圆角刻意小于凹槽,读作"槽里的一片") | TTySegmented |
:hover :selected :disabled |
TyPagination |
分页条(透明,页码本身才是 chrome) | TTyPagination |
:disabled |
TyPaginationItem |
一个页码格 | TTyPagination |
:hover :selected :disabled |
TySteps |
步骤条 | TTySteps |
:disabled |
TyStepsItem |
步骤标记。状态即语义:已完成 = 无伪类、当前 = :selected、未走到 = :disabled,所以基础规则画的是已完成的样子 |
TTySteps |
:hover :selected :disabled |
TyStepsConnector |
步骤间连线,只读 background(刻意不声明 color);取它通向的那一步的状态 |
TTySteps |
:selected :disabled |
TyBreadcrumb |
面包屑条(分隔符取条带自己的墨色) | TTyBreadcrumb |
:disabled |
TyBreadcrumbItem |
一节面包屑;:selected = 最后一节(当前位置,读作普通墨色而非链接) |
TTyBreadcrumb |
:hover :selected :disabled |
TyTransfer |
穿梭框外框(两个列表面与移动键复用 TyListBox / TyButton) |
TTyTransfer |
:disabled |
TyTransferTitle |
穿梭框每一侧的标题带 | TTyTransfer |
— |
TyCascaderPanel |
级联多列面板 | TTyCascaderPanel |
— |
TyCascaderItem |
级联面板中的一行 | TTyCascaderPanel |
:hover :selected :disabled |
TyListGroupHeader |
导航手风琴的分组行(无底色,只有弱化墨色 + 右侧箭头;:selected = 该组已展开) |
TTyListGroupPanel |
:hover :selected |
TyListGroupItem |
导航项;:active = 选中,画成柔和内缩圆角药丸 |
TTyListGroupPanel |
:hover :active :disabled |
TTyTreeSelect没有自己的键,也不该有:它解析TyComboBox(它就是一个下拉树的组合框), 弹出体是真的TTyTreeView、由TyTreeView着色。写TyTreeSelect { … }是永不命中的死 CSS。
任何标识符都可以当变体,只要控件的 StyleClass 属性包含对应 token(空格分隔、可多个)即可匹配。
上表里的"变体"列只是内置主题实际定义过的那些:
- 按钮族的
.primary/.danger/.ghost; TyCaptionButton的.close/.min/.max(由窗体镶边自动赋给三个标题栏按钮);TyTag的.accent/.danger;TyAlert/TyNotification的.info/.success/.warning/.error—— 这四个是硬契约,控件的AlertType/NotificationType直接映射到这四个名字并据此画图标。
注意 §4.4:变体层在状态层之前应用,所以变体的悬停必须写成 Ty….变体:hover。
下面这些键代码会解析,基础层刻意不给定义。它们不是漏网,而是可选钩子: 每一个在绘制侧都有明确的兜底,而且兜底是依状态而变的 —— 在这里写死一个值会移动像素, 而不是保持像素。皮肤可以任意声明它们,基础层保持沉默好让兜底继续生效。
| typeKey | 谁解析 | 不定义的理由 / 兜底 |
|---|---|---|
TyGridPanel |
TTyGridPanel |
格子布局宿主不是一块面。没有主题背景时 DrawFrame 走 TyFillParentBg,于是格子之间的间隔取父容器的颜色,而不是在承载它的东西上戳出一块白面板。tests/test.gridpanel.pas 断言没有任何内置主题给这个键背景。想要可见的网格面,自己写 TyGridPanel { background: …; border: … } 即可 |
TyFormSurface |
TTyFormSurface |
**它没有自己的 CSS。**Surface 渲染的是宿主窗体的 form 背景(见其 Paint),并承载各自解析样式的子控件。中性键的意义是:不要让 TyForm 的 padding / border 意外套到 surface 自己的布局上 |
TyListViewLine |
TTyListView |
background → 否则取列表视图外框的 border-color |
TyListViewMarquee |
TTyListView |
background → 否则取 TyListViewItem:selected 的背景(色相跟主题走、alpha 是常数) |
TyValueListEditorKey |
TTyValueListEditor |
color → 否则取该行当前状态下的行色 |
TyValueListEditorValue |
TTyValueListEditor |
同上 |
TyValueListEditorExpander |
TTyValueListEditor |
同上 |
TyValueListEditorDivider |
TTyValueListEditor |
background → 否则取行色的 alpha 0x28 |
后四个是锋利边缘:选中行的兜底墨色是
--on-accent,所以在这里写一句平的color: var(--on-surface)会让选中行的键名变得读不出来。要声明就把:active/:hover变体一起声明。
b824a49 / d58eada 只做了盒子键。审计同时提出的一批子部件键被刻意推迟——
它们是可换肤性的扩展而不是覆盖缺口,而且其中数个会移动像素(代码目前在本该由键供色的
地方自己发明了一个颜色)。
不要为下面这些名字写规则,它们今天什么都不匹配:
TyHtmlLabelLink、TyButtonGroupItem、TyUpDownButton、
TyChartTitle / TyChartLegend / TyChartAxis / TyChartGrid / TyChartLabel / TyChartSeries1..8、
TyCalculatorDisplay / TyCalculatorExpression、TyBevelHighlight / TyBevelShadow、
TySizeBoxDot、TyControlBarGripper、TyCoolBarGripper、TyColorGridCell、
TyRibbonBackstageSidebar / -Item / -Back / -Separator、
TyRibbonGalleryItem / TyRibbonGalleryPopup、TyTabSetRail、
TyHeaderControlSection / -SortMark / -Divider。
这些控件的对应部位目前不可单独换肤;它们要么借用上表里已有的键,要么颜色/几何是 Pascal 常量。
约四十个控件仍然共用别的控件的 typeKey,这些是逐个审过、有据可查的 (理由见上面那份 plan 文档与两条 commit message)。判据是:皮肤是否应该能把它们分开看待。 不要"修"它们。
| 共用者 | 用的键 | 为什么共用是对的 |
|---|---|---|
TTyMaskEdit、TTyCurrencyEdit、TTyURLEdit、TTyNumericEdit、TTyCalcEdit、TTyCalcCurrencyEdit、TTyValueEdit、TTyComboEdit |
TyEdit |
一个带掩码/带计算器下拉的输入框就是一个输入框。盒子、边框、内距、状态全是 TyEdit 的,差别只在输入过滤与尾部按钮的行为。共用键正是它们在同一行表单里不"起缝"的原因,也让 TyEdit.small 一次穿好全部 |
TTyFontComboBox、TTyFontSizeComboBox、TTyMRUComboBox、TTyColorComboBox、TTyCheckComboBox、TTyComboBoxEx 等 11 个 |
TyComboBox |
同上,它们都是组合框字段 |
TTyTreeSelect |
TyComboBox |
它画的三个记号与 TTyComboBox 逐一相同;下拉体是真的 TTyTreeView,由 TyTreeView 着色 |
TTyRadioGroup、TTyCheckGroup |
TyGroupBox |
分组框就是"一圈带标题的框",框里是各自有键的真控件 |
TTyShellTreeView |
TyTreeView(整族) |
外壳适配器自己不画一个像素;目录树就是树 |
TTyShellListView |
TyListView(整族) |
同上 |
TTyDrawGrid、TTyStringGrid |
TyGrid(整族) |
三层结构里只有 TTyCustomGrid 画像素 |
TTyRibbonPage |
TyRibbon |
页面画的是 ribbon 盒子的严格子集,且刻意不画边框;给它另一种颜色会在每一页周围露出 1px 的 ribbon 本色 |
TTyRelativePanel |
TyPanel |
它一个像素都不画(无 Paint、无 RenderTo),全部来自 TTyPanel.RenderTo;它只是一种布局策略,而布局策略不是皮肤需要区分的视觉身份 |
TTyPaintPanel |
TyPanel |
与普通面板逐字节兼容,它就是面板 |
TTyScrollPanel |
TyScrollBox(继承) |
它加的是手势不是表面;不多画任何东西,共用滚动井的键就是正确结果。注意它不再是 TyPanel —— 基类已经拆走 |
TTyGlyphButton |
TyButton |
它就是一个内容区被切开的按下式按钮 |
TTyGalleryGrid(画廊弹出网格) |
TyListBox |
见 §8.2.14 的推迟说明 |
TTyHeaderControl 的列头单元格 |
TyTreeHeaderSection |
见 §8.2.9 —— 尚未拆分,不是"设计如此" |
下列名字在部分随库样式表里有定义,但 source/ 里没有任何解析点。给它们写规则不会有任何效果。
(注意覆盖面并不一致:TyGridSelection 只出现在 light.tycss 一份里,TyTabControl 与
TyDateTimeButton 各出现在 6 份里 —— 所以"每份主题都有"这句是不成立的。)
| typeKey | 情况 |
|---|---|
TyTabControl |
TTyTabControl 这个类已被删除(commit d201419);该键只作为 TyTabSet 的并列选择器残留 |
TyDateTimeButton |
TTyDateTimePicker 的下拉按钮从不解析它 |
TyGridSelection |
网格实际用的是 TyGridSelectionFrame |
TyTreeSelect |
TTyTreeSelect 返回 'TyComboBox'(有意共用,见 §8.5)。该规则块 3.0 起已从 light.tycss 删除,现在连"有定义"都谈不上 —— 纯死名字 |
控件内部那些不属于"控件本体框"的小部件,按其是不是一块独立的着色面分两类处理颜色, 这是官方约定而非遗留缺口:
-
tier-a — 着色面(colored surfaces): thumb / knob / fill / pointer / needle 等本身就是一块需要 独立配色的实心面。它们各有专属子部件 typeKey(
TyScrollThumb、TyToggleKnob、TyTrackThumb、TyProgressFill,以及 §8.2.15 里那十四个…Fill/ 指针键), 颜色取该 typeKey 的background、圆角取其border-radius, 与父控件本体的background/color互不干扰。主题作者可单独为它们(及其:hover/:active)配色。 -
tier-b — 单色字形(monochrome glyphs): 复选框勾号 / 单选圆点、ComboBox 下拉箭头、 SpinEdit 上下箭头、ScrollBar 端部箭头、Tab 关闭 × 等只是一笔单色墨迹,没有独立的面。 它们借用所属控件(或子部件)样式的
color(TextColor)作为墨色——这是官方约定, 不是"应当各给一个 typeKey 却漏了"。为这类字形配色,直接改对应控件 typeKey 的color即可 (例如TyScrollBar { color: … }改端部箭头墨色、TyTab { color: … }改关闭 × 墨色)。
个别 tier-a 部件会同时叠加一个 tier-b 字形:页签关闭 × 的底片是 tier-a(
TyTabClose的background),其上的 × 笔画是 tier-b(TyTab的color)。
引擎层限制(均已在上文相应小节展开):
-
无组合选择器:不支持后代/子/通配/多变体/纯类/纯状态选择器(§4.2)。
-
重复选择器后写者胜,且是逐属性合并(§4.4)——与浏览器一致。 真正与直觉相反的是基础层的全有全无:主题只要提到某 typeKey 一次, 内置默认层对该键整体失效(§8.1),所以拆出来的新键必须逐个点名。
-
url()路径不能含空格:重建文件名时空格被无条件删除(§5.2); 路径相对进程工作目录解析,缺失文件静默跳过。 -
shadow颜色必须单 token:#hex/var(--x)/ 裸--x,不能用带逗号的 颜色函数;需要半透明用#rrggbbaa(§5.12)。 -
opacity与shadow全控件生效(v1.1):v1.1 修复了TyCheckBox与TyRadioButton的渲染路径,使其也支持opacity和shadow;所有 typeKey 均已生效。 -
alpha()第二参数不带%时是 0..1 小数:alpha(#fff, 50)是完全不透明, 半透明写0.5或50%(§6.3)。 -
渐变角度方向与 CSS 不同:
0deg左→右,90deg上→下(§7.1); 只有线性渐变,方向只能写角度,不支持to right这类关键字(§7)。 -
font-size数值按 pt 解释,px后缀只是装饰(§5.9);font-weight渲染只分 ≥600 粗体 / 其余常规两档(§5.10)。 -
font-family不要加引号,引号会保留进字体名(§5.8)。 -
子部件着色分 tier-a / tier-b(Batch ④):thumb / knob / fill 等着色面由专属子部件 typeKey(
TyScrollThumb、TyToggleKnob、TyTrackThumb、TyProgressFill)的background着色;勾号 / 箭头 / 关闭 × 等单色字形借用所属控件的color(TextColor)作墨色。二者均为官方约定(§8.7)。TyScrollThumb/TyToggleKnob的默认值与早前借用color渲染时逐字一致。 -
页签溢出可横向滚动(v1.10):所有页签头宽度之和超过控件宽度时,页签头条带进入溢出模式可横向滚动——条带左右两端渲染
tgArrowLeft/tgArrowRight箭头按钮,鼠标在条带上滚轮也能滚动,且切换选中页时会自动把目标页签滚入可见区。绘制时页签头被裁剪到两个箭头之间的可见带(箭头始终绘制在最上层)。这套条带引擎由TTyPageControl与TTyTabSet共用;详见 controls/pagecontrol.md。 (原文此处写的是TyTabControl—— 该控件类已删除,该 typeKey 现为死键,见 §8.6。) -
不支持
@media、!important、转义字符串、//行注释。@import(必须先于所有:root/规则)与@mode是支持的。 -
边框/盒模型的取舍(v1.6,v3 起加 3D 斜角):
border-style支持none/solid/outset/inset(后两者为两色 3D 斜角,见 5.4),无dashed、dotted、groove、ridge;不存在margin属性(外边距请用容器布局实现)。border简写只是border-width/border-style/border-color三者的合写,并不引入额外能力。 -
border-radius不支持的形式:每角长手写法(border-top-left-radius等)、 百分比半径、椭圆双半径语法(<a> / <b>)均不支持(§5.6)。outline-offset单独声明时不激活焦点环——必须同时声明outline(§5.13)。 -
度量令牌(metric tokens,v3):虽然主题不能做通用布局(无
width/height/margin/gap属性,控件尺寸与排布归.lfm/LCL),但部分控件的内在几何可用:root里的命名长度令牌调整,控件按名读取、未设时回退到内置常数(故 golden 不变):--checkbox-size/--checkbox-gap— 复选框指示框边长 / 与标题的间距(默认 16 / 6);--radio-size/--radio-gap— 单选钮同上(圆点随框尺寸等比缩放);--groupbox-caption-height— GroupBox 顶部标题带高度(默认 16)。--track-remove-distance— 多滑块控件里,滑块垂直于轨道拖出控件边缘多远算「拖出去了」,松手即删(默认 24)。--listview-text-margin/--listview-icon-gap— 列表视图报表行内容的起点 / 图标与文字的 间距(默认 4 / 2);--treeview-margin/--treeview-icon-gap— 树主列的前导留白 / 文字前的间距(默认 4 / 2)。 这两组还可以只给一个控件设,见 §3.4。--memo-tab-size— Memo 的制表位间距,写法同 CSS 的tab-size:写数字是几个空格宽,写长度 (如32px)是像素(默认 8 个空格)。也可以只给一个 Memo 设,见 §3.4。--popover-move-distance/--popover-show-duration/--popover-hide-duration— 浮层移入 走的距离(默认 8)、显示动画和淡出的毫秒数(默认 150 / 100,不带单位)。也可以只给一个 浮层设,见 §3.4。--line-height— 多行标题的行框高度(logical px)。没有内置常数:未设时用字体 自己的行框,所以加上这个令牌之前一切不动;设了就是行数 × 该值,多出来的部分作为行距 平分到每行上下。控件由此派生的高度下限(文字块高 + 上下 padding)也跟着走—— 主题把font-size和--line-height一起调小,下限就一起降下来。 值可为16、16px或var()。(后续版本会陆续把更多写死几何提升为度量令牌。)
-
字形覆盖令牌(glyph override,v3):勾号/圆点等内置矢量字形可用图标字体码点替换。 在
:root里写--glyph-<槽>: "字体族名" "\<码点>";——族名须为系统已安装或应用已注册 的字体(自带.ttf的url()加载为后续扩展),码点为十六进制(前导\可省)。当前支持的槽:--glyph-check/--glyph-check-indeterminate— 复选框勾号 / 中间态;--glyph-radio— 单选钮圆点;--glyph-close/--glyph-minimize/--glyph-maximize/--glyph-restore— 标题栏按钮图标;--glyph-arrow-up/--glyph-arrow-down/--glyph-arrow-left/--glyph-arrow-right— SpinEdit / 滚动条箭头;--glyph-chevron-down/--glyph-chevron-right/--glyph-chevron-left/--glyph-chevron-up— 展开/折叠 chevron:树、Grid 树列、列表视图分组、ListGroupPanel、ExPanel、属性检查器(ValueListEditor / PropertyGrid)、菜单的子菜单标记、级联选择的分支行、Ribbon 折叠钮(-up/-down)。-left是-right的镜像伙伴:RTL 网格里折叠三角朝阅读前进的方向, 走的就是这个槽(见docs/controls/grid.md);--glyph-dropdown— 下拉字段的指示 V:组合框族、级联、树选择、日期选择、下拉 / 拆分按钮、 工具条下拉按钮、Ribbon 画廊、Grid 下拉单元格(都经TyDrawDropChevron,一条规则全部生效)。
展开标记的大小与墨色:上面两组 chevron 是同一个标记——90° 开口、圆头圆角、线宽为大小的 5/28、 顶点对齐整像素。
--glyph-chevron-size是它中心线的长边(默认 7,现代密度 8;以前只管下拉 V、 默认 9),槽位只决定位置、不决定大小;--glyph-chevron-alpha(0..255,默认 200)把它画得比 旁边的文字浅一些,设 255 即与文字同色。这组取值是对照 Windows 11、macOS、GNOME 同角色标记取的 共同区间(墨迹约为旁边正文大写字母高度的 0.9 倍)。下拉字段的按钮区:组合框族、组合编辑框、日期选择(下拉模式)、树选择、级联的按钮区宽
--field-button-width加两侧各--field-drop-pad(默认 5,即 18 + 10 = 28),下拉 V 在其中 居中、离右框约 10 px(Windows 11 约 16、GNOME 13、macOS 6.5–12;以前在 18 宽的区里居中,只有 5)。微调框的上下按钮只用--field-button-width,不加这个余量。文本字段的左右内距:内置主题(light、auto、dark、system、green)里编辑框(及基于它的组合编辑框、 掩码编辑框、数字 / 货币 / 浮点微调框等)、组合框族、微调框、级联、日期选择的
padding都是var(--pad-field),默认4px 9px(现代密度8px 12px)。加上 1 px 边框,文字离外框 10 px,和下拉 V 离右框一样(以前用--pad-control的 4 px,文字只离 5 px;日期选择用--pad-datetime,离 7 px)。 在:root里改这一个令牌就能统一调所有字段;复选框、标签页、菜单仍读--pad-control,不跟着动。 内置皮肤保留各自的字段内距。令牌名遵循
--glyph-<kind>约定,内部字形绘制统一走一个可覆盖入口,所以其余字形槽(日历翻页等) 如需也能一行接入——只是这些内部字形一般无人换肤,暂未接。 例:--glyph-check: "Segoe MDL2 Assets" "\e73e";。未设时用内置矢量字形。注意:设了有效覆盖 就一定用图标字体渲染(即便该字体/码点渲染为空);只有未设或格式错误才回退矢量。 -
render-style家族预设(v3):控件属性render-style: flat | bevel3d | inset3d;——bevel3d/inset3d让DrawFrame自动应用凸起/凹陷两色 3D 斜角 + 方角 + 默认边宽(2)+ 边色缺省从(纯色)背景派生,省去在每个控件上重复写border-style/border-width/border-color/border-radius。flat(默认)= 原有行为。只作用于走DrawFrame的控件外框 (按钮/输入框/面板/进度条轨道/窗体等),以及页签(TyTab)、滚动条拇指(TyScrollThumb)、 滑块拇指与滑槽(TyTrackThumb/TyTrackGroove);复选框指示框等其他单独绘制的子部件不受其影响。 示例皮肤见examples/theming/classic.tycss(经典 3D 风)。