一个会跟着壁纸变色的 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 支持与配置 |
视觉验收应覆盖短候选、长候选、不同缩放、浅深主题和菜单。代码检查能排除无效切片与缺失产物,不能代替实际渲染;本文的九宫格图是机制示意,不是运行截图。
维护上还存在一个容易遗漏的地方:基础配置与浅深模板里重复的非动态布局字段,需要同步调整。若长期维护多个变体,可以把布局提取为共同来源,再生成各份配置;这是减少重复的改进方向,并不表示当前项目已经完成这一重构。把动态色与固定布局的责任分清,之后改动才不会互相覆盖。
参考与延伸
- 主题项目结构与使用说明(固定版本) ↗
- Fcitx ClassicUI 绘制与蒙版源码 ↗
- Fcitx 5 官方主题格式 ↗
- 零面积切片的维护者说明 ↗
- 主题的 Matugen 模板 ↗
- Matugen 模板登记示例 ↗
- 主题 Home Manager 模块(固定版本) ↗
根据技术笔记重新整理,示例使用通用名称。