feat: app-style ToolHub settings home
This commit is contained in:
@@ -47,6 +47,7 @@ function FloatBallAppWM(logger) {
|
||||
toolAppActive: false,
|
||||
toolAppNavStack: [],
|
||||
toolAppRoute: null,
|
||||
settingsGroupKey: null,
|
||||
|
||||
mask: null,
|
||||
maskLp: null,
|
||||
|
||||
@@ -334,6 +334,7 @@ FloatBallAppWM.prototype.hideAllPanels = function() {
|
||||
this.state.toolAppActive = false;
|
||||
this.state.toolAppRoute = null;
|
||||
this.state.toolAppNavStack = [];
|
||||
this.state.settingsGroupKey = null;
|
||||
this.hideMask();
|
||||
|
||||
this._clearHeavyCachesIfAllHidden("hideAllPanels");
|
||||
|
||||
@@ -1,6 +1,175 @@
|
||||
// @version 1.0.0
|
||||
FloatBallAppWM.prototype.getSettingsGroupDefs = function() {
|
||||
return [
|
||||
{ key: "ball", title: "悬浮球", desc: "大小、图标、透明度、球与面板间距", sections: ["悬浮球"] },
|
||||
{ key: "panel", title: "面板", desc: "行列、间距、文字、位置与吸边", sections: ["面板布局", "面板文字", "吸边与位置"] },
|
||||
{ key: "theme", title: "主题与外观", desc: "明暗主题、文字色、背景与透明度", sections: ["外观"] },
|
||||
{ key: "motion", title: "动画与手势", desc: "吸边动画、回弹、点击/长按手势", sections: ["动画", "触摸与手势"] },
|
||||
{ key: "debug", title: "日志与调试", desc: "日志开关、DEBUG 与保留天数", sections: ["日志"] }
|
||||
];
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.getSettingsGroupDef = function(key) {
|
||||
var defs = this.getSettingsGroupDefs();
|
||||
var k = String(key || "");
|
||||
for (var i = 0; i < defs.length; i++) {
|
||||
if (defs[i] && String(defs[i].key) === k) return defs[i];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.getSettingsGroupTitle = function(key) {
|
||||
var d = this.getSettingsGroupDef(key);
|
||||
return d ? d.title : "设置分组";
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.isSchemaSectionInSettingsGroup = function(sectionName, groupKey) {
|
||||
var d = this.getSettingsGroupDef(groupKey);
|
||||
if (!d || !d.sections) return false;
|
||||
var n = String(sectionName || "");
|
||||
for (var i = 0; i < d.sections.length; i++) {
|
||||
if (String(d.sections[i]) === n) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.createSettingsHomeEntry = function(parent, title, desc, actionText, onClick) {
|
||||
var self = this;
|
||||
var isDark = this.isDarkTheme();
|
||||
var C = this.ui.colors;
|
||||
var cardColor = isDark ? C.cardDark : C.cardLight;
|
||||
var textColor = isDark ? C.textPriDark : C.textPriLight;
|
||||
var subTextColor = isDark ? C.textSecDark : C.textSecLight;
|
||||
var row = new android.widget.LinearLayout(context);
|
||||
row.setOrientation(android.widget.LinearLayout.HORIZONTAL);
|
||||
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||
row.setPadding(this.dp(14), this.dp(12), this.dp(12), this.dp(12));
|
||||
row.setBackground(this.ui.createRoundDrawable(cardColor, this.dp(14)));
|
||||
try { row.setElevation(this.dp(2)); } catch(eElev) { safeLog(null, 'e', "catch " + String(eElev)); }
|
||||
|
||||
var texts = new android.widget.LinearLayout(context);
|
||||
texts.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||
var tvTitle = new android.widget.TextView(context);
|
||||
tvTitle.setText(String(title || ""));
|
||||
tvTitle.setTextColor(textColor);
|
||||
tvTitle.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15);
|
||||
tvTitle.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||
texts.addView(tvTitle);
|
||||
var tvDesc = new android.widget.TextView(context);
|
||||
tvDesc.setText(String(desc || ""));
|
||||
tvDesc.setTextColor(subTextColor);
|
||||
tvDesc.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
|
||||
tvDesc.setPadding(0, this.dp(3), this.dp(8), 0);
|
||||
try { tvDesc.setSingleLine(false); } catch(eSL) { safeLog(null, 'e', "catch " + String(eSL)); }
|
||||
texts.addView(tvDesc);
|
||||
var textLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
textLp.weight = 1;
|
||||
row.addView(texts, textLp);
|
||||
|
||||
var tvGo = new android.widget.TextView(context);
|
||||
tvGo.setText(String(actionText || "进入") + " ›");
|
||||
tvGo.setTextColor(C.primary);
|
||||
tvGo.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
tvGo.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||
row.addView(tvGo);
|
||||
row.setOnClickListener(new android.view.View.OnClickListener({ onClick: function(v) {
|
||||
try { self.touchActivity(); } catch(eT) {}
|
||||
try { if (onClick) onClick(); } catch(eC) { try { self.toast("打开失败: " + String(eC)); } catch(eToast) {} }
|
||||
}}));
|
||||
var lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp.setMargins(this.dp(2), this.dp(6), this.dp(2), this.dp(6));
|
||||
parent.addView(row, lp);
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.buildSettingsHomePanelView = function() {
|
||||
if (!this.state.pendingUserCfg) this.beginEditConfig();
|
||||
var self = this;
|
||||
var isDark = this.isDarkTheme();
|
||||
var C = this.ui.colors;
|
||||
var bgColor = isDark ? C.bgDark : C.bgLight;
|
||||
var subTextColor = isDark ? C.textSecDark : C.textSecLight;
|
||||
var panel = this.ui.createStyledPanel(this, 16);
|
||||
|
||||
var quick = new android.widget.LinearLayout(context);
|
||||
quick.setOrientation(android.widget.LinearLayout.HORIZONTAL);
|
||||
quick.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||
quick.setPadding(0, 0, 0, this.dp(6));
|
||||
|
||||
var memTv = new android.widget.TextView(context);
|
||||
memTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 10);
|
||||
memTv.setTextColor(subTextColor);
|
||||
function updateMem() {
|
||||
try {
|
||||
var rt = java.lang.Runtime.getRuntime();
|
||||
var total = rt.totalMemory() / 1024 / 1024;
|
||||
var free = rt.freeMemory() / 1024 / 1024;
|
||||
var used = total - free;
|
||||
var max = rt.maxMemory() / 1024 / 1024;
|
||||
memTv.setText("Mem " + used.toFixed(0) + "/" + max.toFixed(0) + "M");
|
||||
} catch(e) { memTv.setText("Mem ?"); }
|
||||
}
|
||||
updateMem();
|
||||
memTv.setOnClickListener(new android.view.View.OnClickListener({ onClick: function() { updateMem(); self.toast("内存已刷新"); }}));
|
||||
quick.addView(memTv);
|
||||
quick.addView(this.ui.createSpacer(this));
|
||||
|
||||
var btnDoc = this.ui.createFlatButton(this, "文档", C.primary, function() {
|
||||
try {
|
||||
var intent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
|
||||
intent.setData(android.net.Uri.parse("https://xin-blog.com/114.html"));
|
||||
intent.setFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
} catch(e) { self.toast("无法打开文档链接"); }
|
||||
});
|
||||
quick.addView(btnDoc);
|
||||
|
||||
var btnSave = this.ui.createSolidButton(this, "保存", C.primary, android.graphics.Color.WHITE, function() {
|
||||
try {
|
||||
self.touchActivity();
|
||||
var r = self.commitPendingUserCfg();
|
||||
self.state.previewMode = false;
|
||||
if (self.state.addedPanel) self.hideMainPanel();
|
||||
if (self.state.toolAppActive && self.closeToolApp) self.closeToolApp();
|
||||
else self.hideSettingsPanel();
|
||||
if (r && r.ok) self.toast("已确认并生效");
|
||||
else self.toast("确认失败: " + (r && r.reason ? r.reason : (r && r.err ? r.err : "unknown")));
|
||||
} catch(e0) { try { self.toast("确认异常: " + String(e0)); } catch(eT) {} }
|
||||
});
|
||||
btnSave.setPadding(this.dp(12), this.dp(6), this.dp(12), this.dp(6));
|
||||
quick.addView(btnSave);
|
||||
panel.addView(quick);
|
||||
|
||||
var scroll = new android.widget.ScrollView(context);
|
||||
try { scroll.setOverScrollMode(android.view.View.OVER_SCROLL_NEVER); } catch(eOS) { safeLog(null, 'e', "catch " + String(eOS)); }
|
||||
try { scroll.setVerticalScrollBarEnabled(false); } catch(eSB) { safeLog(null, 'e', "catch " + String(eSB)); }
|
||||
var box = new android.widget.LinearLayout(context);
|
||||
box.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||
box.setPadding(0, this.dp(2), 0, this.dp(8));
|
||||
scroll.addView(box);
|
||||
scroll.setOnTouchListener(new JavaAdapter(android.view.View.OnTouchListener, { onTouch: function(v, e) { self.touchActivity(); return false; }}));
|
||||
|
||||
this.createSettingsHomeEntry(box, "按钮管理", "新增、编辑、排序、启用/禁用工具按钮", "管理", function() { self.pushToolAppPage("btn_editor"); });
|
||||
this.createSettingsHomeEntry(box, "布局管理", "自定义设置项 schema,适合高级调整", "管理", function() { self.pushToolAppPage("schema_editor"); });
|
||||
var defs = this.getSettingsGroupDefs();
|
||||
for (var i = 0; i < defs.length; i++) {
|
||||
(function(d) {
|
||||
self.createSettingsHomeEntry(box, d.title, d.desc, "设置", function() {
|
||||
if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(d.key);
|
||||
});
|
||||
})(defs[i]);
|
||||
}
|
||||
|
||||
panel.addView(scroll);
|
||||
return panel;
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.buildSettingsPanelView = function() {
|
||||
this.beginEditConfig();
|
||||
if (!this.state.settingsGroupKey) return this.buildSettingsHomePanelView();
|
||||
return this.buildSettingsGroupPanelView();
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.buildSettingsGroupPanelView = function() {
|
||||
if (!this.state.pendingUserCfg) this.beginEditConfig();
|
||||
|
||||
var isDark = this.isDarkTheme();
|
||||
var C = this.ui.colors;
|
||||
@@ -38,31 +207,7 @@ FloatBallAppWM.prototype.buildSettingsPanelView = function() {
|
||||
|
||||
var self = this;
|
||||
|
||||
// # 固定按钮:项目文档(放在第一位)
|
||||
var btnDoc = this.ui.createFlatButton(this, "📖 文档", C.primary, function() {
|
||||
try {
|
||||
var intent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
|
||||
intent.setData(android.net.Uri.parse("https://xin-blog.com/114.html"));
|
||||
intent.setFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
} catch(e) {
|
||||
self.toast("无法打开文档链接");
|
||||
}
|
||||
});
|
||||
btnDoc.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
header.addView(btnDoc);
|
||||
|
||||
// [恢复] 按钮管理
|
||||
var btnMgr = this.ui.createFlatButton(this, "按钮管理", C.primary, function() {
|
||||
if (self.state.toolAppActive && self.pushToolAppPage) {
|
||||
self.pushToolAppPage("btn_editor");
|
||||
} else {
|
||||
self.hideSettingsPanel();
|
||||
self.showPanelAvoidBall("btn_editor");
|
||||
}
|
||||
});
|
||||
btnMgr.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
header.addView(btnMgr);
|
||||
// 分组页顶部只保留预览与保存,文档/按钮管理已移动到设置首页入口。
|
||||
|
||||
// 预览模式开关
|
||||
var previewBox = new android.widget.LinearLayout(context);
|
||||
@@ -154,7 +299,9 @@ FloatBallAppWM.prototype.buildSettingsPanelView = function() {
|
||||
}));
|
||||
|
||||
var schema = this.getConfigSchema();
|
||||
var activeGroupKey = String(this.state.settingsGroupKey || "");
|
||||
var currentCard = null;
|
||||
var includeSection = false;
|
||||
|
||||
function createCard() {
|
||||
var c = new android.widget.LinearLayout(context);
|
||||
@@ -173,10 +320,13 @@ FloatBallAppWM.prototype.buildSettingsPanelView = function() {
|
||||
for (var i = 0; i < schema.length; i++) {
|
||||
(function(item) {
|
||||
if (item && String(item.type) === "section") {
|
||||
includeSection = self.isSchemaSectionInSettingsGroup(String(item.name || ""), activeGroupKey);
|
||||
if (!includeSection) { currentCard = null; return; }
|
||||
currentCard = createCard();
|
||||
box.addView(currentCard);
|
||||
self.createSectionHeader(item, currentCard);
|
||||
} else {
|
||||
if (!includeSection) return;
|
||||
if (!currentCard) {
|
||||
currentCard = createCard();
|
||||
box.addView(currentCard);
|
||||
|
||||
@@ -77,7 +77,7 @@ FloatBallAppWM.prototype.buildViewerPanelView = function(titleText, bodyText) {
|
||||
|
||||
// =======================【面板构建:主面板 / 设置面板】======================
|
||||
FloatBallAppWM.prototype.buildPanelView = function(panelType) {
|
||||
if (panelType === "settings") return this.buildSettingsPanelView();
|
||||
if (panelType === "settings" || panelType === "settings_group") return this.buildSettingsPanelView();
|
||||
if (panelType === "btn_editor") return this.buildButtonEditorPanelView();
|
||||
if (panelType === "schema_editor") return this.buildSchemaEditorPanelView();
|
||||
|
||||
@@ -480,12 +480,13 @@ FloatBallAppWM.prototype.addPanel = function(panel, x, y, which) {
|
||||
// =======================【设置类 UI:App 页面栈实验框架】======================
|
||||
FloatBallAppWM.prototype.isToolAppRoute = function(route) {
|
||||
var r = String(route || "");
|
||||
return r === "settings" || r === "btn_editor" || r === "schema_editor";
|
||||
return r === "settings" || r === "settings_group" || r === "btn_editor" || r === "schema_editor";
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.getToolAppTitle = function(route) {
|
||||
var r = String(route || "settings");
|
||||
if (r === "settings") return "ToolHub 设置";
|
||||
if (r === "settings_group") return this.getSettingsGroupTitle ? this.getSettingsGroupTitle(this.state.settingsGroupKey) : "设置分组";
|
||||
if (r === "btn_editor") {
|
||||
if (this.state.editingButtonIndex !== null && this.state.editingButtonIndex !== undefined) {
|
||||
return (this.state.editingButtonIndex === -1) ? "新增按钮" : "编辑按钮";
|
||||
@@ -506,6 +507,7 @@ FloatBallAppWM.prototype.closeToolApp = function() {
|
||||
this.state.toolAppActive = false;
|
||||
this.state.toolAppRoute = null;
|
||||
this.state.toolAppNavStack = [];
|
||||
this.state.settingsGroupKey = null;
|
||||
this.hideViewerPanel();
|
||||
} catch (e) { safeLog(this.L, 'e', "closeToolApp fail: " + String(e)); }
|
||||
};
|
||||
@@ -567,6 +569,12 @@ FloatBallAppWM.prototype.showToolApp = function(route, resetStack) {
|
||||
this.showMask();
|
||||
this.state.toolAppActive = true;
|
||||
this.state.toolAppRoute = r;
|
||||
if (r === "settings") this.state.settingsGroupKey = null;
|
||||
if (resetStack && r === "settings") {
|
||||
this.state.pendingUserCfg = null;
|
||||
this.state.pendingDirty = false;
|
||||
this.state.previewMode = false;
|
||||
}
|
||||
if (resetStack || !this.state.toolAppNavStack || !this.state.toolAppNavStack.length) {
|
||||
this.state.toolAppNavStack = [{ route: r }];
|
||||
} else {
|
||||
@@ -603,6 +611,11 @@ FloatBallAppWM.prototype.pushToolAppPage = function(route) {
|
||||
this.showToolApp(route, false);
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.pushToolAppSettingsGroup = function(groupKey) {
|
||||
this.state.settingsGroupKey = String(groupKey || "");
|
||||
this.pushToolAppPage("settings_group");
|
||||
};
|
||||
|
||||
FloatBallAppWM.prototype.replaceToolAppPage = function(route) {
|
||||
if (!this.isToolAppRoute(route)) return;
|
||||
if (!this.state.toolAppNavStack || !this.state.toolAppNavStack.length) this.state.toolAppNavStack = [{ route: String(route) }];
|
||||
@@ -627,7 +640,9 @@ FloatBallAppWM.prototype.popToolAppPage = function(reason) {
|
||||
}
|
||||
this.state.toolAppNavStack.pop();
|
||||
var top = this.state.toolAppNavStack[this.state.toolAppNavStack.length - 1];
|
||||
this.showToolApp(top && top.route ? top.route : "settings", false);
|
||||
var nextRoute = top && top.route ? String(top.route) : "settings";
|
||||
if (nextRoute !== "settings_group") this.state.settingsGroupKey = null;
|
||||
this.showToolApp(nextRoute, false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
safeLog(this.L, 'e', "popToolAppPage fail reason=" + String(reason || "") + " err=" + String(e));
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
"size": 52546
|
||||
},
|
||||
"th_02_core.js": {
|
||||
"sha256": "3d77bcdbe588cd173f6ed4d20c01bea42798acf9a07cd9392f8679f7cccc0ad1",
|
||||
"size": 4099
|
||||
"sha256": "3ccdff641f8cd1e185812f24df0baf65c7749a320d29549340f884575ac4fa9d",
|
||||
"size": 4127
|
||||
},
|
||||
"th_03_icon.js": {
|
||||
"sha256": "717f7f37474d3616c2cd944581455f600020a850ec8812100d0546ec1302c987",
|
||||
@@ -34,8 +34,8 @@
|
||||
"size": 7938
|
||||
},
|
||||
"th_09_animation.js": {
|
||||
"sha256": "e42d72199c13849b3222719dadbcde7fca9ead69c94bf46c01f831e36adb920f",
|
||||
"size": 27794
|
||||
"sha256": "c3a9565d4b6c0e48d7b7592fd7d999e78fbec3c19bb3f5862151149c7598ae27",
|
||||
"size": 27832
|
||||
},
|
||||
"th_10_shell.js": {
|
||||
"sha256": "0ed793079c2f6ba7d29f4c0d411705cb72419f45f572cbe37ed32ac16527a8bc",
|
||||
@@ -54,12 +54,12 @@
|
||||
"size": 20386
|
||||
},
|
||||
"th_14_panels.js": {
|
||||
"sha256": "69840e0f00fe0b049217ed7a4b5db8bff648d2f4c4ea2dcaff3bb1eb15c06263",
|
||||
"size": 218924
|
||||
"sha256": "c65a2ac74f69f3763f5effd8533a27d1e1bd47660c9f2beb8961ad60d012058a",
|
||||
"size": 226409
|
||||
},
|
||||
"th_15_extra.js": {
|
||||
"sha256": "ffcdc8f8e4dda94658b1e32e4c4ed61e58d4525306a4a1697cccf3635b942d28",
|
||||
"size": 70523
|
||||
"sha256": "b48c16ea5cbe5b9033d593eb18ef3028c0d661c65cbf2ad8f3f650ee55bbe2e4",
|
||||
"size": 71280
|
||||
},
|
||||
"th_16_entry.js": {
|
||||
"sha256": "e7c99c3dfbd6aedab05551426955081ae6cae034754f2f557cefa01dc75dc001",
|
||||
@@ -68,5 +68,5 @@
|
||||
},
|
||||
"keyId": "toolhub-targets-2026-rsa3072",
|
||||
"schema": 2,
|
||||
"version": 20260512030604
|
||||
"version": 20260512093034
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
PhbP9/ZXAhTzly673t026tpTy4gozO9x03KIZ8JKNp2X12uN/ueoHcRW8nsun4+Pz8JouTaxM9lR+tO2CGlSFz5kACibZ8dMv2WLnKDqFLFZOYyW9kbrcAaUbZny9j9duk4dqImFwK1qN3CSa+leWC1bnxsO44wlmLXMgyT9WG1fCUVScPzn/37Fxlvg4aDHCYaKJz05ygE9Nmwnxht7aK++AXaomX8G0P0XVRm+8gmHVHKx37AqvxjwtLzogIuh51/Ja8gb/ZC24QuZRACb+zKfjnSuk6vRRy468O0tzkJtNtUllvihIk/EBOmTMShieUSNVCehADXORmK5G1JBeSz7X7Mk+K/avv+NSX7X2OC+lhMPbYEdkmGpYHZe5QY2qGF667XQI4ereaQyJWgy5zC26QKF/jK3f2SJ/IvSS+4vUZAvrhY/2MJ8OK+G7DwIaWILxDLBo1ecwrsFhadsHPAFfr6nCJKaxHCDM10XVrDE2FKdyjjZZwQHDteJ+/L/
|
||||
QeDCiPelzRAPjhtoPyf6861/eQKyfrIqV66PERJbtyu0n8oJsw34JQxQQE5DjMURszMTjV/scS8cxytrSJtb81W1vqE3LGbRwDdVHt6Wdf+n62HMkRGdg7MzBQma5AVVMPsw3ElvfVf5q46nRsgrCmRXJ4j5d3a8SqlTm+HxtsmaDOjGn+mqZynnHFgGwbj0YQtQXYE+9GUt/tk34ffksG30k0+fRkQ71zsCad9XAHrUMHDJvHOCIZNg+rpqYlZDXxZsTyiXSCmb+IcW5G8jZMDo6K5D650a8TYdh1MfcBl7S0qhsuPPNwqxm9SbtWHwoKg4EDlpKNrRRXupm/4kIGRD+VVMYnsmbIUyGwr561JXqD1IgjyuJY52SQIG+ZEaLy77c/1JQ8tuxzufTcKfIyhr5Q/hbnZ6sRZ9eD791q68ZfQ7HZ9Gzv6kD+3rnnvLNC0kwWnpeeZipa9+qu1xbWkBSWj++Fu9ZBD9CHrB30zF7Tqg2gk3XR8bPttonU0M
|
||||
|
||||
Reference in New Issue
Block a user