feat: keep settings detail in tablet landscape panes

This commit is contained in:
7015725
2026-05-22 01:20:32 +08:00
parent 0605201b97
commit 66fa86b37d
6 changed files with 221 additions and 11 deletions

View File

@@ -60,6 +60,8 @@ function FloatBallAppWM(logger) {
toolAppTitleView: null,
toolAppBackButton: null,
settingsGroupKey: null,
settingsHomeSelectedCategoryId: null,
settingsHomeSelectedItemId: null,
mask: null,
maskLp: null,

View File

@@ -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");

View File

@@ -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);

View File

@@ -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;

View File

@@ -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
}

View File

@@ -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