From 66fa86b37d74917f2fe074bfd03980535c3f103b Mon Sep 17 00:00:00 2001 From: 7015725 Date: Fri, 22 May 2026 01:20:32 +0800 Subject: [PATCH] feat: keep settings detail in tablet landscape panes --- code/th_02_core.js | 2 + code/th_05_persistence.js | 2 +- code/th_14_panels.js | 199 ++++++++++++++++++++++++++++++++++++++ code/th_15_extra.js | 9 ++ manifest.json | 18 ++-- manifest.sig | 2 +- 6 files changed, 221 insertions(+), 11 deletions(-) diff --git a/code/th_02_core.js b/code/th_02_core.js index 9cb3f90..fde7563 100644 --- a/code/th_02_core.js +++ b/code/th_02_core.js @@ -60,6 +60,8 @@ function FloatBallAppWM(logger) { toolAppTitleView: null, toolAppBackButton: null, settingsGroupKey: null, + settingsHomeSelectedCategoryId: null, + settingsHomeSelectedItemId: null, mask: null, maskLp: null, diff --git a/code/th_05_persistence.js b/code/th_05_persistence.js index f43ebde..7357888 100644 --- a/code/th_05_persistence.js +++ b/code/th_05_persistence.js @@ -158,7 +158,7 @@ FloatBallAppWM.prototype.setPendingValue = function(k, v) { if (String(k) === "SETTINGS_THEME") { try { if (this.state.toolAppActive && this.replaceToolAppPage) { - this.replaceToolAppPage("settings_group"); + this.replaceToolAppPage(String(this.state.toolAppRoute || "settings_group")); } else { if (this.state.settingsPanel) { this.safeRemoveView(this.state.settingsPanel, "settingsPanel"); diff --git a/code/th_14_panels.js b/code/th_14_panels.js index 9e9f5ac..8188414 100644 --- a/code/th_14_panels.js +++ b/code/th_14_panels.js @@ -411,6 +411,177 @@ FloatBallAppWM.prototype.getSettingsHomeCategoryDefs = function(useMonetHome) { return cats; }; + +FloatBallAppWM.prototype.findSettingsHomeCategoryAndChild = function(cats, itemId) { + var target = String(itemId || ""); + for (var i = 0; i < (cats || []).length; i++) { + var cat = cats[i]; + var children = cat && cat.children ? cat.children : []; + for (var j = 0; j < children.length; j++) { + if (String(children[j].id) === target) return { category: cat, child: children[j] }; + } + } + return null; +}; + +FloatBallAppWM.prototype.buildSettingsGroupDetailPane = function(groupKey, title, desc) { + var self = this; + var isDark = this.isDarkTheme(); + var C = this.ui.colors; + var T = this.getAnimalIslandTheme(); + var cfgTpl = this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; + this.applySettingsTheme(T, isDark, C, cfgTpl); + var spec = this.getSettingsResponsiveSpec ? this.getSettingsResponsiveSpec() : null; + var columns = (spec && spec.isWideWidth) ? 2 : 1; + var cardRadius = spec ? spec.cardRadius : this.dp(18); + var root = new android.widget.LinearLayout(context); + root.setOrientation(android.widget.LinearLayout.VERTICAL); + root.setPadding(0, 0, 0, 0); + + var top = new android.widget.LinearLayout(context); + top.setOrientation(android.widget.LinearLayout.VERTICAL); + top.setPadding(this.dp(2), 0, this.dp(2), this.dp(10)); + var crumb = new android.widget.TextView(context); + crumb.setText("‹ 返回分类"); + crumb.setTextColor(T.primaryDeep); + crumb.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); + crumb.setTypeface(null, android.graphics.Typeface.BOLD); + crumb.setPadding(0, 0, 0, this.dp(4)); + crumb.setOnClickListener(new android.view.View.OnClickListener({ onClick: function(v) { + try { self.touchActivity(); self.state.settingsHomeSelectedItemId = null; if (self.replaceToolAppPage) self.replaceToolAppPage("settings"); } catch(e) {} + }})); + top.addView(crumb, new android.widget.LinearLayout.LayoutParams(-1, -2)); + var tvTitle = new android.widget.TextView(context); + tvTitle.setText(String(title || this.getSettingsGroupTitle(groupKey) || "设置详情")); + tvTitle.setTextColor(T.text); + tvTitle.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 18); + tvTitle.setTypeface(null, android.graphics.Typeface.BOLD); + top.addView(tvTitle, new android.widget.LinearLayout.LayoutParams(-1, -2)); + var tvDesc = new android.widget.TextView(context); + tvDesc.setText(String(desc || "在右侧整理这一组设置,左侧目录保持常驻")); + tvDesc.setTextColor(T.sub); + tvDesc.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); + tvDesc.setPadding(0, this.dp(4), 0, 0); + top.addView(tvDesc, new android.widget.LinearLayout.LayoutParams(-1, -2)); + root.addView(top, new android.widget.LinearLayout.LayoutParams(-1, -2)); + + var scroll = new android.widget.ScrollView(context); + try { scroll.setOverScrollMode(android.view.View.OVER_SCROLL_NEVER); scroll.setVerticalScrollBarEnabled(false); } catch(eOS) {} + var box = columns > 1 ? this.createSettingsGridContainer(columns) : new android.widget.LinearLayout(context); + if (columns <= 1) box.setOrientation(android.widget.LinearLayout.VERTICAL); + box.setPadding(0, this.dp(4), 0, this.dp(20)); + scroll.addView(box); + scroll.setOnTouchListener(new JavaAdapter(android.view.View.OnTouchListener, { onTouch: function(v, e) { self.touchActivity(); return false; }})); + + var schema = this.getConfigSchema(); + var currentCard = null; + var includeSection = false; + function createCard() { + var c = new android.widget.LinearLayout(context); + c.setOrientation(android.widget.LinearLayout.VERTICAL); + c.setBackground(self.ui.createStrokeDrawable(T.card, self.withAlpha(T.stroke, isDark ? 0.22 : 0.30), self.dp(1), cardRadius)); + try { c.setElevation(self.dp(1)); } catch(e) {} + try { c.setClipToOutline(true); } catch(e2) {} + if (columns > 1) { + var glp = new android.widget.GridLayout.LayoutParams(); + glp.width = 0; + glp.height = android.widget.GridLayout.LayoutParams.WRAP_CONTENT; + glp.columnSpec = android.widget.GridLayout.spec(android.widget.GridLayout.UNDEFINED, 1, 1); + glp.setMargins(self.dp(6), self.dp(6), self.dp(6), self.dp(8)); + c.setLayoutParams(glp); + } else { + var lp = new android.widget.LinearLayout.LayoutParams(-1, -2); + lp.setMargins(self.dp(2), self.dp(6), self.dp(2), self.dp(8)); + c.setLayoutParams(lp); + } + c.setPadding(0, 0, 0, self.dp(4)); + return c; + } + var activeGroupKey = String(groupKey || ""); + 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); } + var needDivider = (currentCard.getChildCount() > 0); + if (currentCard.getChildCount() === 1) needDivider = false; + self.createSettingItemView(item, currentCard, needDivider); + } + })(schema[i]); + } + root.addView(scroll, new android.widget.LinearLayout.LayoutParams(-1, 0, 1)); + + var saveRow = new android.widget.LinearLayout(context); + saveRow.setGravity(android.view.Gravity.RIGHT | android.view.Gravity.CENTER_VERTICAL); + saveRow.setPadding(0, this.dp(8), 0, 0); + var btnSave = this.ui.createSolidButton(this, "💾 保存布置", T.primary, T.onPrimary, 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) {} } + }); + var saveLp = new android.widget.LinearLayout.LayoutParams(this.dp(spec && spec.isWideWidth ? 300 : 260), this.dp(48)); + saveRow.addView(btnSave, saveLp); + root.addView(saveRow, new android.widget.LinearLayout.LayoutParams(-1, -2)); + return root; +}; + +FloatBallAppWM.prototype.buildSettingsRouteDetailPane = function(route, title, desc) { + var self = this; + var isDark = this.isDarkTheme(); + var C = this.ui.colors; + var T = this.getAnimalIslandTheme(); + var cfgTpl = this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; + this.applySettingsTheme(T, isDark, C, cfgTpl); + var root = new android.widget.LinearLayout(context); + root.setOrientation(android.widget.LinearLayout.VERTICAL); + root.setPadding(0, 0, 0, 0); + var top = new android.widget.LinearLayout(context); + top.setOrientation(android.widget.LinearLayout.VERTICAL); + top.setPadding(this.dp(2), 0, this.dp(2), this.dp(8)); + var crumb = new android.widget.TextView(context); + crumb.setText("‹ 返回分类"); + crumb.setTextColor(T.primaryDeep); + crumb.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); + crumb.setTypeface(null, android.graphics.Typeface.BOLD); + crumb.setPadding(0, 0, 0, this.dp(4)); + crumb.setOnClickListener(new android.view.View.OnClickListener({ onClick: function(v) { + try { self.touchActivity(); self.state.settingsHomeSelectedItemId = null; if (self.replaceToolAppPage) self.replaceToolAppPage("settings"); } catch(e) {} + }})); + top.addView(crumb, new android.widget.LinearLayout.LayoutParams(-1, -2)); + var tvTitle = new android.widget.TextView(context); + tvTitle.setText(String(title || this.getToolAppTitle(route) || "详情")); + tvTitle.setTextColor(T.text); + tvTitle.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 18); + tvTitle.setTypeface(null, android.graphics.Typeface.BOLD); + top.addView(tvTitle, new android.widget.LinearLayout.LayoutParams(-1, -2)); + var tvDesc = new android.widget.TextView(context); + tvDesc.setText(String(desc || "在右侧窗格内完成这一项设置")); + tvDesc.setTextColor(T.sub); + tvDesc.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); + tvDesc.setPadding(0, this.dp(4), 0, 0); + top.addView(tvDesc, new android.widget.LinearLayout.LayoutParams(-1, -2)); + root.addView(top, new android.widget.LinearLayout.LayoutParams(-1, -2)); + var oldRoute = this.state.toolAppRoute; + try { this.state.toolAppRoute = String(route || ""); } catch(eR) {} + var raw = this.buildPanelView(route); + try { raw.setBackground(null); raw.setElevation(0); } catch(eRaw) {} + try { this.state.toolAppRoute = oldRoute; } catch(eRR) {} + root.addView(raw, new android.widget.LinearLayout.LayoutParams(-1, 0, 1)); + return root; +}; + FloatBallAppWM.prototype.createSettingsMasterMenuItem = function(parent, cat, selected, onClick) { var self = this; var isDark = this.isDarkTheme(); @@ -506,6 +677,11 @@ FloatBallAppWM.prototype.buildSettingsHomePanelView = function() { } function addChildEntry(parent, child) { self.createSettingsHomeEntry(parent, child.title, child.desc, "", function() { + if (useMasterDetail) { + self.state.settingsHomeSelectedItemId = String(child.id || child.key || ""); + renderMasterDetail(); + return; + } if (String(child.kind) === "group") { if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(child.key); } else self.pushToolAppPage(child.key); }); @@ -559,10 +735,33 @@ FloatBallAppWM.prototype.buildSettingsHomePanelView = function() { (function(cat) { self.createSettingsMasterMenuItem(navList, cat, String(cat.id) === String(selectedCat && selectedCat.id), function() { self.state.settingsHomeSelectedCategoryId = String(cat.id); + self.state.settingsHomeSelectedItemId = null; renderMasterDetail(); }); })(cats[mi]); } + + var selectedItemId = String(self.state.settingsHomeSelectedItemId || ""); + var selectedChild = null; + if (selectedItemId) { + var selectedChildren = selectedCat && selectedCat.children ? selectedCat.children : []; + for (var si = 0; si < selectedChildren.length; si++) { + if (String(selectedChildren[si].id) === selectedItemId) selectedChild = selectedChildren[si]; + } + if (!selectedChild) { + self.state.settingsHomeSelectedItemId = null; + selectedItemId = ""; + } + } + if (selectedChild) { + if (String(selectedChild.kind) === "group") { + detailCard.addView(self.buildSettingsGroupDetailPane(selectedChild.key, selectedChild.title, String(selectedCat.title || "") + " / " + String(selectedChild.title || "")), new android.widget.LinearLayout.LayoutParams(-1, -1)); + } else { + detailCard.addView(self.buildSettingsRouteDetailPane(selectedChild.key, selectedChild.title, String(selectedCat.title || "") + " / " + String(selectedChild.title || "")), new android.widget.LinearLayout.LayoutParams(-1, -1)); + } + try { detailCard.setAlpha(0.98); detailCard.animate().alpha(1.0).setDuration(90).start(); } catch(eAnimDetail) {} + return; + } var head = new android.widget.LinearLayout(context); head.setOrientation(android.widget.LinearLayout.HORIZONTAL); head.setGravity(android.view.Gravity.CENTER_VERTICAL); diff --git a/code/th_15_extra.js b/code/th_15_extra.js index 340be71..2c2b280 100644 --- a/code/th_15_extra.js +++ b/code/th_15_extra.js @@ -546,6 +546,7 @@ FloatBallAppWM.prototype.closeToolApp = function() { this.state.toolAppRoute = null; this.state.toolAppNavStack = []; this.state.settingsGroupKey = null; + this.state.settingsHomeSelectedItemId = null; this.hideViewerPanel(); this.state.toolAppRoot = null; this.state.toolAppBody = null; @@ -1249,6 +1250,14 @@ FloatBallAppWM.prototype.replaceToolAppPage = function(route) { FloatBallAppWM.prototype.popToolAppPage = function(reason) { try { var curRoute = this.state.toolAppRoute ? String(this.state.toolAppRoute) : ""; + try { + var specBack = this.getSettingsResponsiveSpec ? this.getSettingsResponsiveSpec() : null; + if (curRoute === "settings" && specBack && specBack.useSideBySide && this.state.settingsHomeSelectedItemId) { + this.state.settingsHomeSelectedItemId = null; + this.showToolApp("settings", false); + return true; + } + } catch(ePaneBack) {} if (curRoute === "btn_editor" && this.state.editingButtonIndex !== null && this.state.editingButtonIndex !== undefined) { this.state.editingButtonIndex = null; this.state.keepBtnEditorState = true; diff --git a/manifest.json b/manifest.json index d5e2d27..f150ab6 100644 --- a/manifest.json +++ b/manifest.json @@ -6,8 +6,8 @@ "size": 56392 }, "th_02_core.js": { - "sha256": "15bb9bfbd19a673d442e221b0a00a456ed5f87af2666b9c73b117d6223faeecd", - "size": 4584 + "sha256": "3c5c498d200e961d48fc9ca3f885475e770ecb32b83ec6a89d23df3f88aed1c9", + "size": 4664 }, "th_03_icon.js": { "sha256": "717f7f37474d3616c2cd944581455f600020a850ec8812100d0546ec1302c987", @@ -18,8 +18,8 @@ "size": 41840 }, "th_05_persistence.js": { - "sha256": "7d913b2caddf04c41ca13023c06763c03a7e372bb31080af37e7a3b2aa81d5f8", - "size": 15157 + "sha256": "8cc52d3dacc8ac3c1b5aecf6fc399a2fe516402955799eddbd07dc9195c10d40", + "size": 15192 }, "th_06_icon_parser.js": { "sha256": "25b95a5df634a7ee359f3ab798e4d3154a71c24016f7b4bf8a658096644b2484", @@ -54,12 +54,12 @@ "size": 21198 }, "th_14_panels.js": { - "sha256": "3c4df811ab2287d86e639f2a24fe408587ad408ae9f19bf8d3296d94fd89ba8c", - "size": 279891 + "sha256": "e13131fea0170a069a61ce73644097132b54436ace979705f572d18e9a0e6c11", + "size": 290242 }, "th_15_extra.js": { - "sha256": "bdb963bdd6b1b5a7b57f6f55f90a3f022cc2df925765f0195aa6f033efbbc7de", - "size": 102108 + "sha256": "a78f59de13c283c9b796e2be3c80d7303e82805cd438a82ba6697c964aa01b7a", + "size": 102533 }, "th_16_entry.js": { "sha256": "6c59d9891cd010647f84c3db93f1cf95c7bbfb758470ea21044bf72eb8ff73d1", @@ -68,5 +68,5 @@ }, "keyId": "toolhub-targets-2026-rsa3072", "schema": 2, - "version": 20260521165944 + "version": 20260521171952 } diff --git a/manifest.sig b/manifest.sig index 43d9153..c8d22fb 100644 --- a/manifest.sig +++ b/manifest.sig @@ -1 +1 @@ -Th3/g8gdmAUPKvEqWaI82a2gm7qzHKeo1AFryizkELTfKa0G51MAMpQm2vgQ/q+5x9zl5xxz1zd92EWaYE/TsWMLbRTvJJ/4INn1DSNulepEwhkLAq/lzjQmhf4y7nRJFftl5Tp0d4HZj2QQshgQVy3RAg+Kf2RADGSWeCfytVpzqU2JqCR2chpecqWcPgzflYfEi6FubMltNO0xVPgxfecRL9xwpQ5/IqLmKMqib3NBv30xu0owPunE8BvI+uJheI5TB6KAuUg0cWLqBoycvlqEC/VCwZvFos7A6pukJUYX0CuhGI1YGsLgC9R961Yh0fzSQzOh+Iqx/R3y6r6VFUSoWA/QgGqSr2smmAlLCtNrhT93rrBs2uQIok3smGVABJ87GppsOE5+yb/ROr1K/eEm5Ty/VbXFFGlNYfuB2nCM8cEqwdCkzAHYBHc1lveTT3Ag/a3UkoyMyQf8aHyfumimizCwq73d4J3h8SIzTCONxlKiRsZVnRJbC7/nH++f +Oi5qaFfuPUyGJheCEN7PQ2M76xQNa49VD7xdrkxeZMPgq5AcpYdZzKFSH/KMn7c5UOFpw0QRZWDqRXjUzAOcBoufHBRFEgfhtz2GUgATG62WmWJo8g8+Y754TGYvgNLc5yh+bpRWgPU8h01yMsMwoiJR/U8MB33b2ZjGivPFS/UybpSnMkwj9c4MoVlPCWjP5K+BzDVJRNWq3gzC9yYLDZIXn+R/skMpR84B6vUANY4LMpIdKTmJ+KPCxPp2VrMPccKXnYAl86XB2Nkn583xsRDMhfHMJrCLXNgp66xN2Vg1Fk50QS8d5DyX2jDzxfwX9af6BxItO2MEjvCRdSKB21eD0nFkESv2dEpxqsa+tPlcvc4vgbPBdOXY3hoPxdjPdAN7HMXy9bXjRnq9OqUaJe0ncFVFD31WdC9s/j3f68Nu1n32Rcjuxe3vHci2eD8pVhWUs7DJdHoh0YTVUt7ou/Vwr7Y8wmfpuiz5CDTfGg+XlIsggJrUjEPL5SlbBo2A