refactor: 精简主题模板为设置页主题二选一

去掉之前多余的6套模板配色(ocean/sunset/forest/mono等),
只保留一个清晰的二选一功能:

- 动物岛风 (默认) — 当前 Animal Island 固定配色,不变
- 系统莫奈色 — 设置页 UI 使用系统 Monet 色系,与主面板一致

改动:
- THEME_TEMPLATE → SETTINGS_THEME (animal/monet)
- 删除 th_04_theme.js 的 getThemeTemplate/applyThemeTemplate
- buildSettingsGroupPanelView 直接根据 SETTINGS_THEME 用
  ui.colors 的 Monet 色构造 T 结构
- setPendingValue 中 SETTINGS_THEME 切换时重建设置页
This commit is contained in:
7015725
2026-05-15 02:24:28 +08:00
parent cb01591369
commit 32a30babcc
6 changed files with 43 additions and 132 deletions

View File

@@ -83,7 +83,7 @@ var ConfigValidator = {
PANEL_PADDING_DP: { type: "int", min: 8, max: 32, default: 12 }, PANEL_PADDING_DP: { type: "int", min: 8, max: 32, default: 12 },
// 主题配置 // 主题配置
THEME_TEMPLATE: { type: "enum", values: ["system", "animal", "ocean", "sunset", "forest", "mono"], default: "system" }, SETTINGS_THEME: { type: "enum", values: ["animal", "monet"], default: "animal" },
THEME_MODE: { type: "enum", values: [0, 1, 2], default: 1 }, THEME_MODE: { type: "enum", values: [0, 1, 2], default: 1 },
THEME_ACCENT_LIGHT: { type: "string", default: "#FF3A86FF" }, THEME_ACCENT_LIGHT: { type: "string", default: "#FF3A86FF" },
THEME_ACCENT_DARK: { type: "string", default: "#FF90CAF9" }, THEME_ACCENT_DARK: { type: "string", default: "#FF90CAF9" },
@@ -755,7 +755,7 @@ var ConfigManager = {
PANEL_LABEL_TOP_MARGIN_DP: 4, PANEL_LABEL_TOP_MARGIN_DP: 4,
PANEL_BG_FALLBACK_HEX: "#EE1E1E1E", PANEL_BG_FALLBACK_HEX: "#EE1E1E1E",
PANEL_BG_ALPHA: 0.85, PANEL_BG_ALPHA: 0.85,
THEME_TEMPLATE: "system", SETTINGS_THEME: "animal",
THEME_MODE: 1, THEME_MODE: 1,
THEME_DAY_BG_HEX: null, THEME_DAY_BG_HEX: null,
THEME_DAY_TEXT_HEX: null, THEME_DAY_TEXT_HEX: null,
@@ -772,13 +772,9 @@ var ConfigManager = {
], ],
defaultSchema: [ defaultSchema: [
{ type: "section", name: "外观" }, { type: "section", name: "外观" },
{ key: "THEME_TEMPLATE", name: "主题模板", type: "single_choice", options: [ { key: "SETTINGS_THEME", name: "设置页主题", type: "single_choice", options: [
{ label: "系统莫奈色", value: "system" }, { label: "动物岛风", value: "animal" },
{ label: "动物岛(绿)", value: "animal" }, { label: "系统莫奈色", value: "monet" }
{ label: "海洋(蓝)", value: "ocean" },
{ label: "日落(橙)", value: "sunset" },
{ label: "森林(绿)", value: "forest" },
{ label: "黑白(极简)", value: "mono" }
]}, ]},
{ key: "THEME_MODE", name: "主题(0跟随/1白/2黑)", type: "int", min: 0, max: 2, step: 1 }, { key: "THEME_MODE", name: "主题(0跟随/1白/2黑)", type: "int", min: 0, max: 2, step: 1 },
{ key: "THEME_DAY_BG_HEX", name: "日间背景色(#RRGGBB)", type: "text" }, { key: "THEME_DAY_BG_HEX", name: "日间背景色(#RRGGBB)", type: "text" },
@@ -874,7 +870,7 @@ var ConfigManager = {
var needReset = false; var needReset = false;
if (s) { if (s) {
var sStr = JSON.stringify(s); var sStr = JSON.stringify(s);
if (sStr.indexOf("ENABLE_SNAP_TO_EDGE") < 0 || sStr.indexOf("ENABLE_ANIMATIONS") < 0 || sStr.indexOf("BALL_IDLE_ALPHA") < 0 || sStr.indexOf("PANEL_POS_GRAVITY") < 0 || sStr.indexOf("single_choice") < 0 || sStr.indexOf("ball_shortx_icon") < 0 || sStr.indexOf("ball_color") < 0 || sStr.indexOf("THEME_TEMPLATE") < 0) { if (sStr.indexOf("ENABLE_SNAP_TO_EDGE") < 0 || sStr.indexOf("ENABLE_ANIMATIONS") < 0 || sStr.indexOf("BALL_IDLE_ALPHA") < 0 || sStr.indexOf("PANEL_POS_GRAVITY") < 0 || sStr.indexOf("single_choice") < 0 || sStr.indexOf("ball_shortx_icon") < 0 || sStr.indexOf("ball_color") < 0 || sStr.indexOf("SETTINGS_THEME") < 0) {
needReset = true; needReset = true;
} }
} else { } else {

View File

@@ -747,67 +747,14 @@ FloatBallAppWM.prototype.safeParseColor = function(hex, fallbackInt) {
try { return android.graphics.Color.parseColor(String(hex)); } catch (e) { return fallbackInt; } try { return android.graphics.Color.parseColor(String(hex)); } catch (e) { return fallbackInt; }
}; };
// =======================【主题模板:一键切换配色预设】======================
// 每个模板定义日间/夜间 背景+文字色。选择模板后如果用户没单独覆盖那4个颜色配置
// 就在 getPanelBgColorInt/getPanelTextColorInt 中自动填充模板值。
FloatBallAppWM.prototype.getThemeTemplate = function(name) {
var tplMap = {
system: { dayBg: null, dayText: null, nightBg: null, nightText: null },
animal: { dayBg: "#A8DDB4", dayText: "#5E472D", nightBg: "#2F4034", nightText: "#FFF1D2" },
ocean: { dayBg: "#B3E5FC", dayText: "#1A237E", nightBg: "#1A237E", nightText: "#B3E5FC" },
sunset: { dayBg: "#FFE0B2", dayText: "#4E342E", nightBg: "#3E2723", nightText: "#FFCCBC" },
forest: { dayBg: "#C8E6C9", dayText: "#1B5E20", nightBg: "#1B5E20", nightText: "#C8E6C9" },
mono: { dayBg: "#F5F5F5", dayText: "#212121", nightBg: "#212121", nightText: "#F5F5F5" }
};
return tplMap[name] || tplMap.system;
};
// 应用主题模板:如果选择非 system 模板且那4个颜色未手动覆盖用模板值填充。
// preview=true 时从 pendingUserCfg 读/写(预览态),否则从 this.config 读/写。
FloatBallAppWM.prototype.applyThemeTemplate = function(preview) {
try {
var cfg = preview && this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config;
var tpl = String(cfg.THEME_TEMPLATE || "system");
if (tpl === "system") return; // system 走 Monet不填充
var t = this.getThemeTemplate(tpl);
var _isNull = function(v) { return v == null || String(v) === "" || String(v) === "null"; };
if (t.dayBg != null && _isNull(cfg.THEME_DAY_BG_HEX)) {
cfg.THEME_DAY_BG_HEX = String(t.dayBg);
}
if (t.dayText != null && _isNull(cfg.THEME_DAY_TEXT_HEX)) {
cfg.THEME_DAY_TEXT_HEX = String(t.dayText);
}
if (t.nightBg != null && _isNull(cfg.THEME_NIGHT_BG_HEX)) {
cfg.THEME_NIGHT_BG_HEX = String(t.nightBg);
}
if (t.nightText != null && _isNull(cfg.THEME_NIGHT_TEXT_HEX)) {
cfg.THEME_NIGHT_TEXT_HEX = String(t.nightText);
}
try { _th_log(this.L, "d", "[theme:template] applied " + tpl + " dayBg=" + String(t.dayBg) + " preview=" + !!preview); } catch(eL) {}
} catch(e) {
try { _th_log(this.L, "e", "[theme:template] apply err=" + String(e)); } catch(eL2) {}
}
};
FloatBallAppWM.prototype.getPanelBgColorInt = function() { FloatBallAppWM.prototype.getPanelBgColorInt = function() {
// 这段代码的主要内容/用途:配合"白天/夜晚"两档主题,返回统一的背景颜色(不再依赖自动亮度推断)。 // 这段代码的主要内容/用途:配合"白天/夜晚"两档主题,返回统一的背景颜色(不再依赖自动亮度推断)。
// 应用主题模板(如果选择模板且用户未手动覆盖)
// 预览态从 pendingUserCfg 读模板值并填充 pendingUserCfg 的颜色字段
var inPreview = this.state.pendingUserCfg != null;
try { this.applyThemeTemplate(inPreview); } catch(eAT) {}
var isDark = this.isDarkTheme(); var isDark = this.isDarkTheme();
// 预览态优先从 pendingUserCfg 读颜色值 var dayBgHex = (this.config.THEME_DAY_BG_HEX != null) ? String(this.config.THEME_DAY_BG_HEX) : null;
var cfg = inPreview && this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; var nightBgHex = (this.config.THEME_NIGHT_BG_HEX != null) ? String(this.config.THEME_NIGHT_BG_HEX) : null;
var dayBgHex = (cfg.THEME_DAY_BG_HEX != null) ? String(cfg.THEME_DAY_BG_HEX) : null;
var nightBgHex = (cfg.THEME_NIGHT_BG_HEX != null) ? String(cfg.THEME_NIGHT_BG_HEX) : null;
// # 兼容旧版默认配色:若仍为旧默认值,自动回退到莫奈色 // # 兼容旧版默认配色:若仍为旧默认值,自动回退到莫奈色
if (dayBgHex === "#FAF4E3") dayBgHex = null; if (dayBgHex === "#FAF4E3") dayBgHex = null;
@@ -835,18 +782,10 @@ FloatBallAppWM.prototype.getPanelBgColorInt = function() {
FloatBallAppWM.prototype.getPanelTextColorInt = function(bgInt) { FloatBallAppWM.prototype.getPanelTextColorInt = function(bgInt) {
// 这段代码的主要内容/用途:配合"白天/夜晚"两档主题,返回统一的文字颜色(不再依赖自动亮度推断)。 // 这段代码的主要内容/用途:配合"白天/夜晚"两档主题,返回统一的文字颜色(不再依赖自动亮度推断)。
// 应用主题模板(如果选择模板且用户未手动覆盖)
// 预览态从 pendingUserCfg 读模板值并填充 pendingUserCfg 的颜色字段
var inPreviewText = this.state.pendingUserCfg != null;
try { this.applyThemeTemplate(inPreviewText); } catch(eAT) {}
var isDark = this.isDarkTheme(); var isDark = this.isDarkTheme();
// 预览态优先从 pendingUserCfg 读颜色值 var dayTextHex = (this.config.THEME_DAY_TEXT_HEX != null) ? String(this.config.THEME_DAY_TEXT_HEX) : null;
var cfgT = inPreviewText && this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; var nightTextHex = (this.config.THEME_NIGHT_TEXT_HEX != null) ? String(this.config.THEME_NIGHT_TEXT_HEX) : null;
var dayTextHex = (cfgT.THEME_DAY_TEXT_HEX != null) ? String(cfgT.THEME_DAY_TEXT_HEX) : null;
var nightTextHex = (cfgT.THEME_NIGHT_TEXT_HEX != null) ? String(cfgT.THEME_NIGHT_TEXT_HEX) : null;
// # 兼容旧版默认配色:若仍为旧默认值,自动回退到莫奈色 // # 兼容旧版默认配色:若仍为旧默认值,自动回退到莫奈色
if (dayTextHex === "#333333") dayTextHex = null; if (dayTextHex === "#333333") dayTextHex = null;

View File

@@ -154,8 +154,8 @@ FloatBallAppWM.prototype.setPendingValue = function(k, v) {
this.state.pendingUserCfg[k] = v; this.state.pendingUserCfg[k] = v;
this.state.pendingDirty = true; this.state.pendingDirty = true;
if (this.state.previewMode) { if (this.state.previewMode) {
// 主题模板切换需要重建整个设置页 UI配色来自 buildSettingsGroupPanelView // 设置页主题切换需要重建整个设置页 UI配色来自 buildSettingsGroupPanelView
if (String(k) === "THEME_TEMPLATE") { if (String(k) === "SETTINGS_THEME") {
try { try {
if (this.state.toolAppActive && this.replaceToolAppPage) { if (this.state.toolAppActive && this.replaceToolAppPage) {
this.replaceToolAppPage("settings_group"); this.replaceToolAppPage("settings_group");

View File

@@ -257,54 +257,30 @@ FloatBallAppWM.prototype.buildSettingsGroupPanelView = function() {
var C = this.ui.colors; var C = this.ui.colors;
var T = this.getAnimalIslandTheme(); var T = this.getAnimalIslandTheme();
// 如果用户选择了非 system 的主题模板,根据模板颜色构造设置页配色替代 Animal Island // 设置页主题切换animal默认动物岛风或 monet系统莫奈色
// 优先从 pendingUserCfg 读模板值(预览态),其次从 this.config
var cfgTpl = this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; var cfgTpl = this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config;
var tpl = String(cfgTpl.THEME_TEMPLATE || "system"); var settTheme = String(cfgTpl.SETTINGS_THEME || "animal");
if (tpl !== "system") { if (settTheme === "monet") {
try { try {
var tplColors = this.getThemeTemplate(tpl);
var Color = android.graphics.Color; var Color = android.graphics.Color;
var dayBg = tplColors.dayBg ? android.graphics.Color.parseColor(tplColors.dayBg) : null; var monetBg = isDark ? C.bgDark : C.bgLight;
var dayText = tplColors.dayText ? android.graphics.Color.parseColor(tplColors.dayText) : null; var monetTxt = isDark ? C.textPriDark : C.textPriLight;
var nightBg = tplColors.nightBg ? android.graphics.Color.parseColor(tplColors.nightBg) : null; var monetCard = isDark ? C.cardDark : C.cardLight;
var nightText = tplColors.nightText ? android.graphics.Color.parseColor(tplColors.nightText) : null; var monetSub = isDark ? C.textSecDark : C.textSecLight;
var bg = isDark ? (nightBg || T.bg) : (dayBg || T.bg); var monetPrimary = C.primary;
var txt = isDark ? (nightText || T.text) : (dayText || T.text); var monetOnP = C._monetOnPrimary || (isDark ? Color.parseColor("#062E6F") : Color.WHITE);
// 根据主背景色推导辅助色:用调亮/调暗/混合方式生成 T 结构的其他字段 T.bg = monetBg;
var r = Color.red(bg), g = Color.green(bg), b = Color.blue(bg); T.card = monetCard;
var lum = (r*0.299 + g*0.587 + b*0.114) / 255.0; T.card2 = monetCard;
var subTxt = isDark ? Color.rgb( T.text = monetTxt;
Math.min(255, Color.red(txt) + 40), T.sub = monetSub;
Math.min(255, Color.green(txt) + 40), T.primary = monetPrimary;
Math.min(255, Color.blue(txt) + 40) T.primaryDeep = monetPrimary;
) : Color.rgb( T.primarySoft = isDark ? this.withAlpha(monetPrimary, 0.20) : this.withAlpha(monetPrimary, 0.10);
Math.max(0, Color.red(txt) - 40), T.brown = monetSub;
Math.max(0, Color.green(txt) - 40), T.stroke = isDark ? this.withAlpha(monetTxt, 0.16) : this.withAlpha(monetTxt, 0.12);
Math.max(0, Color.blue(txt) - 40) T.onPrimary = monetOnP;
); } catch(eM) { safeLog(null, 'e', "catch " + String(eM)); }
var cardBg = isDark ? Color.rgb(
Math.min(255, r + 25), Math.min(255, g + 25), Math.min(255, b + 25)
) : Color.rgb(
Math.max(0, r - 20), Math.max(0, g - 20), Math.max(0, b - 20)
);
var card2 = isDark ? Color.rgb(
Math.max(0, r + 40), Math.max(0, g + 40), Math.max(0, b + 40)
) : Color.rgb(
Math.min(255, r - 30), Math.min(255, g - 30), Math.min(255, b - 30)
);
T.bg = bg;
T.card = cardBg;
T.card2 = card2;
T.text = txt;
T.sub = subTxt;
T.primary = this.ui.colors.primary;
T.primaryDeep = this.ui.colors.primary;
T.primarySoft = isDark ? this.withAlpha(this.ui.colors.primary, 0.20) : this.withAlpha(this.ui.colors.primary, 0.12);
T.brown = txt;
T.stroke = isDark ? this.withAlpha(txt, 0.25) : this.withAlpha(txt, 0.18);
T.onPrimary = this.ui.colors._monetOnPrimary || (isDark ? Color.parseColor("#062E6F") : Color.WHITE);
} catch(eTpl) { safeLog(null, 'e', "catch " + String(eTpl)); }
} }
var bgColor = T.bg; var bgColor = T.bg;

View File

@@ -2,8 +2,8 @@
"alg": "SHA256withRSA", "alg": "SHA256withRSA",
"files": { "files": {
"th_01_base.js": { "th_01_base.js": {
"sha256": "abb3e1ea017874d1ac26979ad784c21f7e398f8473355d0c5697ce7e128408ed", "sha256": "df8a72de4da16452d19ed6a1f704b42e1f090356af52d2d4769f4ea66300313e",
"size": 53143 "size": 52885
}, },
"th_02_core.js": { "th_02_core.js": {
"sha256": "15bb9bfbd19a673d442e221b0a00a456ed5f87af2666b9c73b117d6223faeecd", "sha256": "15bb9bfbd19a673d442e221b0a00a456ed5f87af2666b9c73b117d6223faeecd",
@@ -14,12 +14,12 @@
"size": 5598 "size": 5598
}, },
"th_04_theme.js": { "th_04_theme.js": {
"sha256": "09a6fd1087cffd0a77aa72212f1c7ce15b145bbc9084c0f159e70e02397006c6", "sha256": "b56b9013de31bf191a786359f1371b07375c3aa60bbb15bdf318dd39945858d7",
"size": 41425 "size": 38293
}, },
"th_05_persistence.js": { "th_05_persistence.js": {
"sha256": "a7ffd7f4d5e75dfcb6eeb8ffd8689da251f5ace3cd8c2ffc24397301f616afb8", "sha256": "2aac4db26a0f1969d3b47fd23ce24e1da5cc42658aa5762d52a9f2a1285bd547",
"size": 14950 "size": 14953
}, },
"th_06_icon_parser.js": { "th_06_icon_parser.js": {
"sha256": "25b95a5df634a7ee359f3ab798e4d3154a71c24016f7b4bf8a658096644b2484", "sha256": "25b95a5df634a7ee359f3ab798e4d3154a71c24016f7b4bf8a658096644b2484",
@@ -54,8 +54,8 @@
"size": 20392 "size": 20392
}, },
"th_14_panels.js": { "th_14_panels.js": {
"sha256": "f9d450c8d42dbdca8ef9bb6b4b7556cff7a3da8f841849a9a1d2dc426b24e0d1", "sha256": "9e8200cd2b4855484952501aa5e838e0ee57da83251c57ad7a8ca85c0f169e64",
"size": 246197 "size": 244802
}, },
"th_15_extra.js": { "th_15_extra.js": {
"sha256": "b607620f1900c1bd93ccbec8d901d4de53b3d36e9373877f4264442f79b9b956", "sha256": "b607620f1900c1bd93ccbec8d901d4de53b3d36e9373877f4264442f79b9b956",
@@ -68,5 +68,5 @@
}, },
"keyId": "toolhub-targets-2026-rsa3072", "keyId": "toolhub-targets-2026-rsa3072",
"schema": 2, "schema": 2,
"version": 20260514181134 "version": 20260514182424
} }

View File

@@ -1 +1 @@
Q6SrwKQWn8sqD26NKkAy09VuBvJUMz1nSpv40wzE+/h3rmI+aYaubBL6A3rKK4Jthv94Ql4ulHGheEarHrElailP0JkJE+Pvr6SyGPrlCF2CmDdgCNJBWcI23x6bjATTMDDPj/ELFAbUwrXuSU7CZ5grHYnBjAIDFywkt0BEKeQ6wSqQ/P5uRWwT61CBvGznVblBh0BoaMtTG64TmeveuUmBA8diax3HSw4oGGVWETiMzCwJPmdxLsoxCOZig0eXiOQzkVLnXCVyyoh4rxKzz+espf9H3pjVTXKaNHDmjbbvyhIT6r2VrwbX8Cq2EF3HCfO1WNcvCoKlN0zRwxI6rl8Uz4ESxDeSuD9jHlmFZHH7qerFG/eSfaHTzWVxzDrzZz8zbpctWlmiPiRdfy4g+IGRMVYXxBoFnBA3q5xB0i/euHx46/j/+3lCr1G5e/LknhsK93TQu1k8F5kQaL9ZNHfG4E+WBg7GqMDLh8rj1o7vIzAWf+Agcd+gJjUEZ/m2 XbvlBLrkCDw72fS8i9hgfSmLRA8MoH5FCBn4Vg+OhFIddDkYJoO5spnG2ZoLdhDurSvBeoGPK9vUij0Ps8V6s+L6k6KjzX33LNncwWcYrop3Eho1hvtsJMq4pGZZOqhloqElhrY6dWBiOS4lFlrNAkrKUh/afFVv/5jsrCzJBNZHj06D1wUw5aswaJ0NRQSyhTMBNc3fEYfb5/G6yfNNWy6RfY8V9asbRyOAJbd5IgdB5nhOFt3X6WzQ2rBxmK60S+vOC6zs91uBqkQuzjbpWY7rSbyoPtccub3z3AAEXjAUVYkJ8w5a0XJdgb7vo29aq91s31A0vgBMCIJgCeDPDe+zikOh6suCslwKSRpbMem1m/Ae/xbfRyNW3gunFTFY0b7r3w/DSdNzG2DJOUfraAUMz5WdxzAOnj1vwPRUnYmFCIO8ilQIkQwoFFH63+YCst6KPD4y7PRd9Dc5Gz+Svqb/3q5+3wIro73eSO/eFAUIQtUJAJ2ihQK8um1dgG1t