From c41e1479b91edef741df3d3f9a803d99edb52c76 Mon Sep 17 00:00:00 2001 From: 7015725 Date: Tue, 12 May 2026 17:31:02 +0800 Subject: [PATCH] feat: app-style ToolHub settings home --- code/th_02_core.js | 1 + code/th_09_animation.js | 1 + code/th_14_panels.js | 202 ++++++++++++++++++++++++++++++++++------ code/th_15_extra.js | 21 ++++- manifest.json | 18 ++-- manifest.sig | 2 +- 6 files changed, 206 insertions(+), 39 deletions(-) diff --git a/code/th_02_core.js b/code/th_02_core.js index 3240043..d73c771 100644 --- a/code/th_02_core.js +++ b/code/th_02_core.js @@ -47,6 +47,7 @@ function FloatBallAppWM(logger) { toolAppActive: false, toolAppNavStack: [], toolAppRoute: null, + settingsGroupKey: null, mask: null, maskLp: null, diff --git a/code/th_09_animation.js b/code/th_09_animation.js index 62883e6..83ae079 100644 --- a/code/th_09_animation.js +++ b/code/th_09_animation.js @@ -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"); diff --git a/code/th_14_panels.js b/code/th_14_panels.js index e3f31fb..fe32a29 100644 --- a/code/th_14_panels.js +++ b/code/th_14_panels.js @@ -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); diff --git a/code/th_15_extra.js b/code/th_15_extra.js index 9c3d4bf..ecf0027 100644 --- a/code/th_15_extra.js +++ b/code/th_15_extra.js @@ -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)); diff --git a/manifest.json b/manifest.json index 1792823..3663bad 100644 --- a/manifest.json +++ b/manifest.json @@ -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 } diff --git a/manifest.sig b/manifest.sig index ebf611b..a402f5d 100644 --- a/manifest.sig +++ b/manifest.sig @@ -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