主题系统(ThemeAPI / IThemeApi)
基本信息
| 属性 | 值 |
|---|---|
| 包路径 | com.cleanroommc.modularui.theme(18 个文件)+ api/ITheme.java、api/IThemeApi.java |
| 入口 | IThemeApi.get() → ThemeAPI.INSTANCE(api/IThemeApi.java:107-109) |
| 加载器 | ThemeManager(theme/ThemeManager.java:42,@SideOnly(Side.CLIENT)) |
| 主题实现 | AbstractTheme(:98 行处)→ DefaultTheme、SelectableTheme |
| 重载时机 | 注册为资源重载监听器(ClientProxy.java:180) |
| 指令 | /reloadThemes(theme/ThemeReloadCommand.java:16-17,extends CommandBase) |
功能
主题的三层结构
DEFAULT(DefaultTheme.INSTANCE,Java 硬编码兜底)
↑ parent 继承
JSON 主题文件(assets/<任意域>/themes/<name>.json)
↑ 由 themes.json 索引
themes.json 索引(assets/<任意域>/themes.json)
真实外观数据在 Java 里(DefaultTheme 由 ThemeAPI 的部件键推导默认值),
JSON 只是覆盖层。仓库自带的 3 个 JSON 主题很薄:
| 文件 | 行数 | 内容 |
|---|---|---|
assets/modularui2/themes/vanilla.json |
2(无末尾换行) | 仅 {"parent": "DEFAULT"} |
assets/modularui2/themes/vanilla_dark.json |
9 | 覆盖 color: 0x444444、textColor: 0xEEEEEE、textShadow: false、scrollbar 子对象(color: 0x666666、textColor: 0xCCCCCC) |
assets/modularui2/themes/context_menu.json |
27 | parent: DEFAULT + panel / button / toggleButton 三个部件覆盖 |
主题索引 themes.json
src/main/resources/assets/modularui/themes.json 的完整内容:
{
"vanilla": "modularui2:vanilla",
"vanilla_dark": "modularui2:vanilla_dark",
"modularui.context_menu": "modularui2:context_menu",
"screens": { "modularui:test": "vanilla_dark" }
}
- 3 个主题名 → 主题资源位置
screens段把界面 id 映射到主题(ThemeManager.loadScreenThemes,由ThemeManager.java:81调用)
⚠️ 该文件位于
assets/modularui/(旧的modularui域), 而主题 JSON 本身在assets/modularui2/themes/。 这不是笔误但也非必需:AssetHelper.findAssets(String)(utils/AssetHelper.java:31-37)会遍历所有资源域查找themes.json, 放在哪个域都能被发现。放在modularui域的效果是原版 ModularUI 也能读到这些主题。
主题查找优先级
ThemeAPI.getThemeIdForScreen(String mod, String name, String panelName)
(theme/ThemeAPI.java:72-91)按以下顺序查找,命中即返回:
mod:name:panel(JSON 声明)mod:name(JSON 声明)mod(JSON 声明,整 mod 默认)mod:name:panel(Java 声明)mod:name(Java 声明)mod(Java 声明)- 都没有 →
ModularUIConfig.useDarkThemeByDefault ? "vanilla_dark" : "vanilla"(:69)
对外 API
IThemeApi(api/IThemeApi.java)标注 @ApiStatus.NonExtendable(:30),
暴露 8 个方法/属性:
| 成员 | 行 | 用途 |
|---|---|---|
FALLBACK 部件键 |
:34 |
绝对兜底部件主题 |
registerTheme(String, JsonBuilder) |
:138 |
Java 侧注册主题(资源包优先级更高) |
registerTheme(ThemeBuilder<?>) |
:145 |
上者的构建器重载 |
getJavaDefaultThemes(String) |
:155 |
取某主题已注册的 JSON 列表 |
getThemeForScreen(owner, name, panel, defaultTheme, fallbackTheme) |
:178 |
按界面取主题 |
registerThemeForScreen(String screen, String theme) |
:220 |
Java 侧绑定界面→主题 |
registerWidgetTheme(id, default, defaultHover, parser) |
:231 |
注册自定义部件主题 |
getWidgetThemeKeys() |
:238 |
取全部已注册部件键 |
部件主题 id 有格式校验:必须匹配 [a-zA-Z0-9$_-]+(ThemeAPI.java:25),
否则抛 IllegalArgumentException(ThemeAPI.java:111);重复注册抛 IllegalStateException
(ThemeAPI.java:108)。
交互
| 触发 | 行为 |
|---|---|
| 客户端资源重载 | ThemeManager.reload()(ThemeManager.java:47):发 ReloadThemeEvent.Pre → 清空主题 → 重新扫描 → 校验祖先链 → 发 ReloadThemeEvent.Post |
/reloadThemes 指令 |
ThemeReloadCommand 手动重载 |
| 其他 mod 注册主题 | IThemeApi.get().registerTheme(id, jsonBuilder) |
ThemeAPI.onReload()(:135)每次重载都会themes.clear()+jsonScreenThemes.clear()并只重新放入DEFAULT_THEME(:136-138), 所以其他 mod 必须在资源重载后重新注册自己的主题。