跳转到内容

SusSkin ​

SusSkin

皮肤机制 — 将已发现的皮肤描述符应用于活动的 VisualElement 树:layout 样式表无论皮肤开或关都保持挂载,因此 .sharq 组合类继续解析;brand 样式表(令牌/覆盖)仅在皮肤启用时挂载 — 这正是「关闭 → 默认 SUS 外观」的含义。可选的 component 层在皮肤启用期间把小型覆盖样式表带入每个组件。根/表面类用普通 USS 切换,在 glass 与 paper 表面之间切换。

包

SusKit · 服务(商业)

SusSkin 是普通的 C# 静态类,不是 .sharq 组件。Kit 本身从不命名具体皮肤 — 每个皮肤都以自己的 描述符资产(SusSkinAsset)交付,并通过同一机制接入,无论是免费的仅布局皮肤还是付费品牌皮肤集。

使用 ​

csharp
// Fluent SusApp entry — layout sheets always, brand + component sheets and fonts only when enabled
SusApp.Create(document)
    .UseSkin(mySkinAsset, enabled: true)
    .Run();

// Re-apply on a live root (surface defaults to the descriptor's DefaultSurface)
SusSkin.Apply(root, mySkinAsset, enabled: true, SusSkinSurface.Glass);

// Remove the skin entirely — classes off, layout, brand AND component sheets detached
SusSkin.Off(root, mySkinAsset);
成员角色
UseSkin(skin, enabled)Fluent SusApp 入口 — 始终挂载 layout 样式表,启用时挂载 brand、component 样式表和字体
Apply(root, skin, enabled, surface?)切换根/表面类,在活动根上(重新)挂载样式表,添加或移除 component 层 — 幂等
Off(root, skin)分离全部内容,包括 layout 样式表和 component 层 — 用于彻底离开树
AttachLayoutSheets(element, skin)仅 layout 样式表,用于挂在应用根面板之外的子树

皮肤描述符 ​

皮肤就是数据:由 SusSkinAsset 解析的 sus-skin.<id>.json 描述符(schema sus-skin/v1)。 样式表条目是 Resources 路径。

json
{
  "schema": "sus-skin/v1",
  "id": "myskin",
  "class": "myskin-skin",
  "sheets": {
    "layout":    ["SusRuntime/myskin-screens"],
    "brand":     ["SusRuntime/myskin-tokens", "SusRuntime/myskin-overrides"],
    "component": ["SusRuntime/myskin-component-overrides"]
  },
  "surfaces": { "glass": "myskin-skin--glass", "paper": "myskin-skin--paper" },
  "defaultSurface": "glass"
}
层挂载到何时放什么
sheets.layout皮肤根只要皮肤存在,无论开或关皮肤自身 .sharq 外框的组合类
sheets.brand皮肤根仅在皮肤启用时设计令牌(custom properties)以及写在皮肤类下的覆盖
sheets.component(可选)皮肤根下每个带样式的组件,位于该组件自身生成样式表之后仅在皮肤启用时不带皮肤类前缀、只含覆盖的小型样式表 — 不含令牌

没有 sheets.component 的描述符会让每个组件的样式表与无皮肤时完全相同。

品牌规则如何胜出 ​

UI Toolkit 在不同样式表的规则冲突时先比较特异性。只有特异性相同时,挂载在层级更低处的样式表才胜出。 每个带样式的 Kit 组件都在组件元素本身上携带自己的生成样式表(.g.uss),因此根上的品牌规则若与 Kit 规则特异性相同,就会输给它 — Kit 外观保持不变。

让品牌规则胜出有两种方式。

默认:在皮肤类下编写覆盖 ​

给每条覆盖加上皮肤的根类(描述符的 class)作为前缀。多出的一个类把特异性提到 Kit 的单类规则之上, 于是规则从根上胜出。这在运行时没有开销,也不需要额外的层。

css
/* Loses: (0,1,0) on the root ties with (0,1,0) on the component itself */
.sus-menu-button { border-width: 1px; }

/* Wins: (0,2,0) beats the kit's (0,1,0) */
.myskin-skin .sus-menu-button { border-width: 1px; }

当 Kit 规则本身是复合选择器时,要在前缀下重复整个 Kit 选择器。此时单类前缀不够: .myskin-skin .sus-menu-button 是 (0,2,0),与 Kit 的 .sus-menu-button--flat.sus-menu-button--color-primary 相同,而平局归组件自身的样式表。

css
/* Wins: (0,3,0) beats the kit's compound (0,2,0) */
.myskin-skin .sus-menu-button--flat.sus-menu-button--color-primary { background-color: var(--myskin-accent); }

关闭皮肤(Apply(root, skin, enabled: false))会移除皮肤类和 brand 样式表,所有带前缀的覆盖都不再匹配, Kit 外观随之恢复。

可选:component 层 ​

按 Web CSS 方式编写的皮肤 — 覆盖不带皮肤类前缀,期望特异性相同的规则胜出 — 把这些样式表列在 sheets.component 中。皮肤启用期间,SusSkin 会把它们追加到皮肤根下每个带样式组件自身生成样式表之后, 于是特异性相同的规则胜出:

css
/* In a sheets.component sheet, on a SusMenuButton that carries the class my-screen__back-btn:
   (0,1,0) now beats the kit's .sus-menu-button (0,1,0) */
.my-screen__back-btn { border-width: 1px; }
  • 只放覆盖,不放令牌。 该层会在每个嵌套层级追加,所以请保持精简。在 sheets.brand 中于根上设置的 custom properties 本来就会继承到每个组件。
  • 关闭即恢复 Kit 外观。 禁用或移除皮肤会从所有被添加过该层的组件上移除它。
  • 把皮肤应用到应用根。 覆盖层(菜单、对话框、提示)只有在覆盖层位于皮肤所应用的根之下时才会得到该层 — 在 SusApp 上使用 UseSkin,或在 SusApp.Root 上使用 Apply。直接挂载到覆盖层、位于皮肤根之外的组件得不到该层;从皮肤根之下的组件打开的覆盖层则会得到。
  • 之后创建的组件在挂载到面板时会拾取该层。

注意 ​

  • Surface 是二选一 — SusSkinSurface.Glass 或 SusSkinSurface.Paper — 在皮肤描述符中声明为 DefaultSurface,可在每次 Apply 调用时覆盖。
  • Storybook story Skin (skin):发现项目链接的任意皮肤包(与 Skins 窗口相同),并与默认 SUS 外观并排显示,带有「Skin enabled」开关和 glass/paper 表面切换。
  • Hub shot kit-skin.png — 展示(待拍摄)。