OFFSET偏移
← 文章目录OFFSET / FIELD NOTESNO. 004

从素材到候选窗:一个动态主题的完整链路。

Fcitx5 的九宫格、布局与模糊蒙版,如何接上 Matugen 配色和 Nix 运行目录。

所在专题桌面之下,谁在工作? ↗
约 15 分钟阅读开始阅读

一个会跟着壁纸变色的 Fcitx5 主题,至少有三件事要一起成立:素材能够伸展,颜色生成到正确文件,输入法读到那份运行配置。只修圆角或只换配色,都可能留下另一半问题。沿着素材、模板与运行目录,把整条链拆开看。

从壁纸到候选窗,经过哪些文件#

以 mellow-matugen 主题项目为例,Matugen 从图片生成调色方案,模板把颜色写入主题配置与高亮素材,ClassicUI 再读取生成后的主题。Nix 和 Home Manager 负责提供包、模板以及可写的运行主题目录;它们并不代替 Matugen 做每次图片配色。[1]

图片 → Matugen 调色方案
              ├─ theme.conf.tpl → theme.conf
              └─ highlight.svg.tpl → highlight.svg
                                      ↓
                        Fcitx5 ClassicUI 选择并读取主题

Nix 包 / Home Manager → 静态素材、模板、可写运行目录

主题目录里,theme.conf 组织布局与素材引用;panel.svg 提供面板背景;highlight.svg 绘制候选高亮;blur-mask.svg 提供模糊区域蒙版。模板目录里的文件是生成输入,Fcitx 最终读取的是生成后的配置和素材,不会替你展开模板占位符。

这一项目选择让高亮与文字色动态生成,部分背景和蒙版维持静态。其他主题可以采用不同拆分,不应把这份项目结构误认成所有 Fcitx 主题的强制格式。

图片、颜色与布局参数各管哪一层#

ClassicUI 主题的 InputPanel 与 Menu 分别描述候选面板和菜单。下面摘取候选面板相关配置,展示字段之间的关系;它不是一份独立可用的完整主题,图片文件和其他必要字段仍需配齐。

[InputPanel]
HighlightCandidateColor=#ffffff
HighlightColor=#ffffff

[InputPanel/Background]
Image=panel.svg

[InputPanel/Background/Margin]
Left=14
Right=14
Top=14
Bottom=14

[InputPanel/Highlight]
Image=highlight.svg

[InputPanel/ContentMargin]
Left=9
Right=9
Top=7
Bottom=7

[InputPanel/TextMargin]
Left=9
Right=9
Top=6
Bottom=7

背景绘制首先要分清使用图像还是配置色。设置 Image 时,相应背景主要走图像素材的绘制路径;修改 Color 或 BorderColor 不会自动重写 SVG 中的 fill。这也是“模板颜色变了,但窗口看起来没变”的常见来源:改动没有落在真正控制那块像素的文件上。[2]

文字色也有具体作用对象。选中候选的文字色需要与高亮背景一起设计;面板普通文字、候选高亮、菜单高亮则不能因为名字相似就全部当作同一层。检查配置时应沿实际绘制对象找字段。

九宫格决定哪里可以伸展#

背景图放大时,并非简单把整张图等比拉伸。九宫格把它分成四角、四边和中心:角部保留形状,上下边沿水平方向伸展,左右边沿垂直方向伸展,中间区域则在两个方向上缩放。高亮背景也使用相关规则。[3]

切片机制示意,不按像素比例绘制。

渲染器认识切分线,不会自动识别图里的圆角。若曲线跨进可伸展区域,曲线的一部分也会跟着拉长。SVG 能平滑缩放,不等于它能跳过这套切片规则;素材里的渐变、描边和阴影也要考虑边界。

30 减去 15,再减去 15#

假设加载后的素材是 30 × 30,四边切片边距都设为 15,留给中心的尺寸就是:

源中心宽度 = 30 − 15 − 15 = 0
源中心高度 = 30 − 15 − 15 = 0

这已经不是“中心很窄”,而是没有面积的中心。Fcitx 维护者在公开问题中指出这种参数不合理,并建议将各边减去 1。这里的 14 是针对这组尺寸的修正,不能成为所有圆角素材的通用数字。[4]

四边设为 14:
源中心 = (30 − 14 − 14) × (30 − 14 − 14) = 2 × 2

若目标为 100 × 40:
目标中心 = (100 − 14 − 14) × (40 − 14 − 14) = 72 × 12

现在可伸展区域有了实际取样面积。本文引用的源码会跳过宽或高不大于零的源切片,因此应将零面积情况描述为切片退化,而不是凭直觉断言发生“除零”。几何关系说明参数是否合理,最终边框外观还取决于素材以及渲染结果。[2]

对 SVG 还要核对加载器取得的固有尺寸。viewBox 是内部坐标系,不能直接把它当作这里的像素宽高;本文核对的加载路径读取固有像素尺寸。只看到 viewBox 写着 30 × 30,就把所有计算建立在 30 上,可能从第一步便用错数据。

三类 Margin,不是在重复设留白#

字段主要职责先检查的现象
Background/Margin决定素材切片边界圆角、边框被拉坏
ContentMargin安排内容区域外围的空间内容离面板边缘过近或过远
TextMargin参与文字条目的布局与尺寸文字周围留白、高亮与文字关系

这三类数字可能偶然相同,但不能互相替代。为了让文字往里挪就不断增大背景切片边距,可能没有解决布局,反而让中心退化。候选面板与菜单又有各自参数;修好面板后,仍需单独观察菜单。[3]

配色模板需要同时覆盖背景和文字#

Matugen 的配色角色有对应关系:例如 primary 可用于强调背景,on_primary 用于其上的内容。若只更换高亮背景而保持原文字色,浅色与深色方案切换后就容易失去对比。

下面是模板中的两处摘录,分别属于不同文件。SVG 的路径数据省略了,不能把这段摘录当成完整 SVG 文件。

# theme.conf.tpl:选中候选的文字色
HighlightCandidateColor={{colors.on_primary.light.hex}}

# highlight.svg.tpl:高亮素材中的填充色
<path fill="{{colors.primary.light.hex}}" d="..." />

浅色主题固定取 light,深色主题固定取 dark,就能一次生成成对主题。default 则跟随本次选择的模式;若本来要维护一浅一深两套文件,却都用 default,两个输出就可能取到同一模式。当前项目模板采用明确分支,形状与非动态布局则维持原有结构。[5]

登记输出时,不能只注册 theme.conf 而漏掉 highlight.svg。下面四段 TOML 分别登记两套主题的两个产物;所有 /path/to/... 都是待替换的绝对路径,应指向实际模板位置与可写运行主题目录。

[templates.fcitx5-light-theme]
input_path = "/path/to/templates/mellow-matugen/theme.conf.tpl"
output_path = "/path/to/themes/mellow-matugen/theme.conf"

[templates.fcitx5-light-highlight]
input_path = "/path/to/templates/mellow-matugen/highlight.svg.tpl"
output_path = "/path/to/themes/mellow-matugen/highlight.svg"

[templates.fcitx5-dark-theme]
input_path = "/path/to/templates/mellow-matugen-dark/theme.conf.tpl"
output_path = "/path/to/themes/mellow-matugen-dark/theme.conf"

[templates.fcitx5-dark-highlight]
input_path = "/path/to/templates/mellow-matugen-dark/highlight.svg.tpl"
output_path = "/path/to/themes/mellow-matugen-dark/highlight.svg"

Matugen 的模板登记负责把同一调色方案渲染到各输出。下面是从图片生成的示例,需先准备好配置与可写输出目录:[6]

matugen image /path/to/wallpaper.png -m dark -t scheme-content

生成后先读文件:theme.conf 是否仍残留花括号占位符,highlight.svg 的填充色是否实际变化,浅深两套是否分别取了正确角色。配置里有正确颜色,与 Fcitx 已经加载这份配置,仍是两个检查点。

不可变输入与可写输出,需要两个位置#

Nix 包可以把主题与模板安装到 store 中作为固定输入,但 Matugen 不能把动态结果直接写进不可变 store。Home Manager 模块为运行主题准备可写副本,模板输入则可以继续来自包提供的文件。

下面的片段要求项目已作为名为 fcitx5-matugen-theme 的 flake input,并且 inputs 已传入相应 Home Manager 模块。它展示该项目的接口,不涉及自动执行系统激活。

# 已将项目作为 flake input,并将 inputs 参数传给模块时
imports = [ inputs.fcitx5-matugen-theme.homeManagerModules.default ];
programs.fcitx5-matugen = {
  enable = true;
  themeSet = "both";
  style = "blur";
  installMatugenTemplates = true;
  installRuntimeThemes = true;
};

ClassicUI 随后需要选择实际主题目录名。下面是配置键摘录,应合并进现有受管理配置;其中并没有额外的 section 标题。

Theme=mellow-matugen
DarkTheme=mellow-matugen-dark
UseDarkTheme=True

在本文固定的项目版本中,Home Manager 每次激活会重新复制运行主题。因此,可写并不代表保留上一次生成结果:完成激活后,还需要按自己的流程重新生成动态颜色。若不清楚这层覆盖关系,就会把“激活刷新了素材”误诊为 Matugen 随机失效。[7]

还要区分主题确实被生成、配置确实指向它、运行中的 ClassicUI 确实加载它。多个同名主题来源、浅深主题选项和更新时机,都可能使屏幕上的结果与刚查看的文件不同。

模糊蒙版也继承素材的几何约束#

相关源码构造模糊蒙版配置时,会复制背景配置,再替换蒙版图像并清除叠加图。也就是说,背景切片边距会参与蒙版处理,蒙版并不是脱离背景几何规则的另一张任意图片。素材尺寸与圆角区域应一起核对。[2]

蒙版描述请求模糊的区域,最终效果还依赖 compositor 支持、相关协议以及用户配置。面板透明、蒙版文件存在,都不能单独证明桌面模糊已经生效。检查时应把主题图像渲染与 compositor 模糊分开观察。

按产物定位问题,再检查整个候选窗#

现象优先核对
圆角或边框异常固有尺寸、切分线、中心是否有面积
文字位置不合适TextMargin、ContentMargin 及面板与菜单各自参数
文字变色,高亮仍旧highlight.svg 是否登记、生成并被当前主题读取
生成时报写入失败输出是否指向 store、目录是否可写
生成正确,屏幕仍旧当前选中的主题、浅深选项与加载时机
激活后恢复初始配色运行副本是否被刷新,是否需要重新生成
透明却没有模糊蒙版几何、compositor 支持与配置

视觉验收应覆盖短候选、长候选、不同缩放、浅深主题和菜单。代码检查能排除无效切片与缺失产物,不能代替实际渲染;本文的九宫格图是机制示意,不是运行截图。

维护上还存在一个容易遗漏的地方:基础配置与浅深模板里重复的非动态布局字段,需要同步调整。若长期维护多个变体,可以把布局提取为共同来源,再生成各份配置;这是减少重复的改进方向,并不表示当前项目已经完成这一重构。把动态色与固定布局的责任分清,之后改动才不会互相覆盖。

参考与延伸

  1. 主题项目结构与使用说明(固定版本) ↗
  2. Fcitx ClassicUI 绘制与蒙版源码 ↗
  3. Fcitx 5 官方主题格式 ↗
  4. 零面积切片的维护者说明 ↗
  5. 主题的 Matugen 模板 ↗
  6. Matugen 模板登记示例 ↗
  7. 主题 Home Manager 模块(固定版本) ↗

根据技术笔记重新整理,示例使用通用名称。

沿专题继续阅读

桌面之下,谁在工作?

  1. 01rwx 的另一面。
  2. 02登录之后,谁来接管桌面?
  3. 03从素材到候选窗:一个动态主题的完整链路。
CONTINUE READING / 下一篇配置可重现,应用为什么还会“失忆”?

从 GNOME 默认值、Home Manager 激活到 Flatpak 管理范围,分清声明、偏好与数据。