refactor: polish schema editor blueprint UI

This commit is contained in:
7015725
2026-05-23 02:12:28 +08:00
parent 52b13bea19
commit c62328c419
3 changed files with 346 additions and 281 deletions

View File

@@ -1,327 +1,392 @@
// ToolHub - Schema 编辑器模块 // ToolHub - 高级蓝图编辑器模块
// 依赖th_14_panels.js 的设置页基础 UI / showPanelAvoidBallth_05_persistence.js 的 ConfigManager。 // 依赖th_14_panels.js 的设置页主题/基础 UIth_05_persistence.js 的 ConfigManager。
// 注意:加载顺序必须位于 th_14_panels.js 之后th_15_extra.js 之前。 // 加载顺序th_14_panels.js 之后th_15_extra.js 之前。
// =======================【Schema 编辑面板】======================
FloatBallAppWM.prototype.buildSchemaEditorPanelView = function() { FloatBallAppWM.prototype.buildSchemaEditorPanelView = function() {
var self = this; var self = this;
if (this.state.editingSchemaIndex === undefined) { if (this.state.editingSchemaIndex === undefined) this.state.editingSchemaIndex = null;
this.state.editingSchemaIndex = null;
}
if (!this.state.keepSchemaEditorState || !this.state.tempSchema) { if (!this.state.keepSchemaEditorState || !this.state.tempSchema) {
var current = ConfigManager.loadSchema(); var current = ConfigManager.loadSchema();
this.state.tempSchema = JSON.parse(JSON.stringify(current)); this.state.tempSchema = JSON.parse(JSON.stringify(current));
} }
this.state.keepSchemaEditorState = false; this.state.keepSchemaEditorState = false;
var schema = this.state.tempSchema; var schema = this.state.tempSchema || [];
var isEditing = (this.state.editingSchemaIndex !== null); var isEditing = (this.state.editingSchemaIndex !== null && this.state.editingSchemaIndex !== undefined);
var isDark = this.isDarkTheme(); var isDark = this.isDarkTheme();
var C = this.ui.colors; var C = this.ui.colors;
var T = this.getAnimalIslandTheme ? this.getAnimalIslandTheme() : null;
try { if (this.applySettingsTheme) this.applySettingsTheme(T, isDark, C, this.state.pendingUserCfg || this.config); } catch(eTheme) {}
var bgColor = isDark ? C.bgDark : C.bgLight; var bgColor = T && T.bg ? T.bg : (isDark ? C.bgDark : C.bgLight);
var cardColor = isDark ? C.cardDark : C.cardLight; var cardColor = T && T.card ? T.card : (isDark ? C.cardDark : C.cardLight);
var textColor = isDark ? C.textPriDark : C.textPriLight; var textColor = T && T.text ? T.text : (isDark ? C.textPriDark : C.textPriLight);
var subTextColor = isDark ? C.textSecDark : C.textSecLight; var subTextColor = T && T.sub ? T.sub : (isDark ? C.textSecDark : C.textSecLight);
var dividerColor = isDark ? C.dividerDark : C.dividerLight; var primaryColor = T && T.primary ? T.primary : C.primary;
var primaryDeep = T && T.primaryDeep ? T.primaryDeep : C.primary;
var primarySoft = T && T.primarySoft ? T.primarySoft : self.withAlpha(primaryColor, isDark ? 0.22 : 0.12);
var strokeColor = T && T.stroke ? T.stroke : (isDark ? C.dividerDark : C.dividerLight);
var inputBgColor = isDark ? C.inputBgDark : C.inputBgLight; var inputBgColor = isDark ? C.inputBgDark : C.inputBgLight;
var dangerColor = C.danger || C.error || 0xffd32f2f;
var panel = this.ui.createStyledPanel(this, 16); function lpFullWrap() {
return new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
}
// --- Header --- function addWithMargins(parent, view, l, t, r, b) {
var header = this.ui.createStyledHeader(this, 12); var lp = lpFullWrap();
lp.setMargins(self.dp(l || 0), self.dp(t || 0), self.dp(r || 0), self.dp(b || 0));
parent.addView(view, lp);
}
// Title removed to avoid redundancy with Wrapper Title function makeText(txt, sp, color, bold) {
// var titleTv = new android.widget.TextView(context); var tv = new android.widget.TextView(context);
// titleTv.setText(isEditing ? (this.state.editingSchemaIndex === -1 ? "新增项" : "编辑项") : "设置布局管理"); tv.setText(String(txt || ""));
// ... tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, sp || 13);
// header.addView(titleTv); tv.setTextColor(color || textColor);
if (bold) tv.setTypeface(null, android.graphics.Typeface.BOLD);
return tv;
}
// Placeholder to push buttons to right function makeChip(txt, color, onClick) {
header.addView(this.ui.createSpacer(this)); var tv = new android.widget.TextView(context);
tv.setText(String(txt || ""));
tv.setTextColor(color || primaryDeep);
tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
tv.setGravity(android.view.Gravity.CENTER);
tv.setTypeface(null, android.graphics.Typeface.BOLD);
tv.setMinHeight(self.dp(32));
tv.setPadding(self.dp(10), 0, self.dp(10), 0);
try { tv.setBackground(self.ui.createStrokeDrawable(self.withAlpha(color || primaryColor, isDark ? 0.16 : 0.08), self.withAlpha(color || primaryColor, isDark ? 0.45 : 0.28), self.dp(1), self.dp(16))); } catch(eBg) {}
if (onClick) {
tv.setOnClickListener(new android.view.View.OnClickListener({ onClick: function(v) {
try { self.touchActivity(); } catch(eTouch) {}
try { onClick(v); } catch(eClick) { safeLog(null, 'e', "schema chip click err=" + String(eClick)); }
}}));
}
return tv;
}
function showToast(msg) { try { self.toast(String(msg || "")); } catch(eToast) {} }
function refreshPanel() { function refreshPanel() {
self.state.keepSchemaEditorState = true; self.state.keepSchemaEditorState = true;
self.showPanelAvoidBall("schema_editor"); self.showPanelAvoidBall("schema_editor");
} }
function clearDraft() {
self.state.schemaEditingDraft = null;
self.state.schemaEditingDraftIndex = null;
}
function normalizeItem(item) {
var it = item || {};
it.type = String(it.type || "bool");
if (it.type !== "section") {
it.key = String(it.key || "").trim();
}
it.name = String(it.name || it.key || "").trim();
if (it.type === "section") {
delete it.key; delete it.min; delete it.max; delete it.step; delete it.action;
} else if (it.type === "bool" || it.type === "text") {
delete it.min; delete it.max; delete it.step; delete it.action;
} else if (it.type === "int" || it.type === "float") {
delete it.action;
if (it.min === "" || isNaN(Number(it.min))) delete it.min; else it.min = Number(it.min);
if (it.max === "" || isNaN(Number(it.max))) delete it.max; else it.max = Number(it.max);
if (it.step === "" || isNaN(Number(it.step))) delete it.step; else it.step = Number(it.step);
} else if (it.type === "action") {
delete it.min; delete it.max; delete it.step;
it.action = String(it.action || "").trim();
}
return it;
}
function validateItem(item) {
if (!item.name) return "请填写显示名字";
if (item.type !== "section" && !item.key) return "请填写配置键";
if (item.type === "action" && !item.action) return "请填写动作 ID";
return "";
}
var panel = this.ui.createStyledPanel(this, 16);
try { panel.setBackground(this.ui.createRoundDrawable(bgColor, this.dp(20))); } catch(ePanelBg) {}
if (!isEditing) { if (!isEditing) {
// List Mode var topCard = new android.widget.LinearLayout(context);
header.addView(self.ui.createFlatButton(self, "重置", C.danger, function() { topCard.setOrientation(android.widget.LinearLayout.VERTICAL);
ConfigManager.resetSchema(); topCard.setPadding(self.dp(14), self.dp(12), self.dp(14), self.dp(12));
self.state.tempSchema = null; try { topCard.setBackground(self.ui.createStrokeDrawable(cardColor, self.withAlpha(strokeColor, isDark ? 0.32 : 0.42), self.dp(1), self.dp(20))); } catch(eTopBg) {}
self.toast("已重置为默认布局");
refreshPanel(); var titleRow = new android.widget.LinearLayout(context);
titleRow.setOrientation(android.widget.LinearLayout.HORIZONTAL);
titleRow.setGravity(android.view.Gravity.CENTER_VERTICAL);
var titleBox = new android.widget.LinearLayout(context);
titleBox.setOrientation(android.widget.LinearLayout.VERTICAL);
titleBox.addView(makeText("岛屿蓝图", 17, textColor, true));
titleBox.addView(makeText("这里会改变设置页结构,建议只在需要整理入口时使用", 12, subTextColor, false));
titleRow.addView(titleBox, new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1));
titleRow.addView(makeChip("添加", primaryDeep, function() {
self.state.editingSchemaIndex = -1;
clearDraft();
if (self.state.toolAppActive && self.pushToolAppPage) self.pushToolAppPage("schema_editor");
else refreshPanel();
})); }));
topCard.addView(titleRow);
header.addView(self.ui.createFlatButton(self, "新增", C.primary, function() { var stat = makeText("共 " + schema.length + " 个蓝图项 · 保存后才会生效", 12, subTextColor, false);
self.state.editingSchemaIndex = -1; stat.setPadding(0, self.dp(8), 0, 0);
if (self.state.toolAppActive && self.pushToolAppPage) self.pushToolAppPage("schema_editor"); topCard.addView(stat);
else refreshPanel(); addWithMargins(panel, topCard, 0, 0, 0, 8);
}));
var btnClose = self.ui.createFlatButton(self, "✕", C.textSecLight, function() {
self.state.tempSchema = null;
self.hideAllPanels();
});
header.addView(btnClose);
panel.addView(header);
panel.setTag(header); // 暴露 Header
// Save Button
var btnSaveAll = self.ui.createSolidButton(self, "保存生效", C.primary, android.graphics.Color.WHITE, function() {
ConfigManager.saveSchema(schema);
self.state.tempSchema = null;
self.toast("布局已保存");
if (self.state.toolAppActive && self.popToolAppPage) {
self.state.editingSchemaIndex = null;
self.popToolAppPage("schema_save_all");
} else {
self.hideAllPanels();
self.showPanelAvoidBall("settings");
}
});
var saveLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
saveLp.setMargins(0, 0, 0, self.dp(8));
btnSaveAll.setLayoutParams(saveLp);
panel.addView(btnSaveAll);
// List
var scroll = new android.widget.ScrollView(context); var scroll = new android.widget.ScrollView(context);
try { scroll.setVerticalScrollBarEnabled(false); } catch(e) { safeLog(null, 'e', "catch " + String(e)); } try { scroll.setVerticalScrollBarEnabled(false); scroll.setOverScrollMode(android.view.View.OVER_SCROLL_NEVER); } catch(eScroll) {}
var list = new android.widget.LinearLayout(context); var list = new android.widget.LinearLayout(context);
list.setOrientation(android.widget.LinearLayout.VERTICAL); list.setOrientation(android.widget.LinearLayout.VERTICAL);
list.setPadding(0, self.dp(4), 0, self.dp(4)); list.setPadding(0, self.dp(2), 0, self.dp(10));
if (!schema.length) {
var empty = makeText("蓝图列表为空,可点上方“添加”创建新项。", 13, subTextColor, false);
empty.setGravity(android.view.Gravity.CENTER);
empty.setPadding(self.dp(12), self.dp(24), self.dp(12), self.dp(24));
list.addView(empty, lpFullWrap());
}
for (var i = 0; i < schema.length; i++) { for (var i = 0; i < schema.length; i++) {
(function(idx) { (function(idx) {
var item = schema[idx]; var item = schema[idx] || {};
var card = new android.widget.LinearLayout(context); var card = new android.widget.LinearLayout(context);
card.setOrientation(android.widget.LinearLayout.HORIZONTAL); card.setOrientation(android.widget.LinearLayout.VERTICAL);
card.setGravity(android.view.Gravity.CENTER_VERTICAL); card.setPadding(self.dp(13), self.dp(11), self.dp(13), self.dp(10));
card.setBackground(self.ui.createRoundDrawable(cardColor, self.dp(8))); try { card.setBackground(self.ui.createStrokeDrawable(cardColor, self.withAlpha(strokeColor, isDark ? 0.28 : 0.36), self.dp(1), self.dp(18))); card.setElevation(self.dp(1)); } catch(eCardBg) {}
try { card.setElevation(self.dp(2)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
var cardLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); var row = new android.widget.LinearLayout(context);
cardLp.setMargins(self.dp(2), self.dp(4), self.dp(2), self.dp(4)); row.setOrientation(android.widget.LinearLayout.HORIZONTAL);
card.setLayoutParams(cardLp); row.setGravity(android.view.Gravity.CENTER_VERTICAL);
card.setPadding(self.dp(12), self.dp(12), self.dp(8), self.dp(12)); var badge = makeText(item.type === "section" ? "分组" : String(item.type || "项"), 11, primaryDeep, true);
badge.setGravity(android.view.Gravity.CENTER);
badge.setPadding(self.dp(8), self.dp(3), self.dp(8), self.dp(3));
try { badge.setBackground(self.ui.createStrokeDrawable(primarySoft, self.withAlpha(primaryDeep, 0.25), self.dp(1), self.dp(12))); } catch(eBadge) {}
row.addView(badge);
// Info var info = new android.widget.LinearLayout(context);
var infoBox = new android.widget.LinearLayout(context); info.setOrientation(android.widget.LinearLayout.VERTICAL);
infoBox.setOrientation(android.widget.LinearLayout.VERTICAL); info.setPadding(self.dp(10), 0, 0, 0);
var infoLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); var nameTv = makeText(String(item.name || item.key || "未命名蓝图项"), 14, textColor, true);
infoLp.weight = 1; info.addView(nameTv);
infoBox.setLayoutParams(infoLp); var sub = item.type === "section" ? "分组标题" : ("配置键:" + String(item.key || "未填写"));
info.addView(makeText(sub, 11, subTextColor, false));
row.addView(info, new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1));
card.addView(row);
var nameTv = new android.widget.TextView(context);
nameTv.setText(String(item.name || item.key || "未命名"));
nameTv.setTextColor(textColor);
nameTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14);
if (item.type === "section") nameTv.setTypeface(null, android.graphics.Typeface.BOLD);
infoBox.addView(nameTv);
var typeTv = new android.widget.TextView(context);
var typeTxt = item.type;
if (item.type !== "section") typeTxt += " (" + (item.key || "?") + ")";
typeTv.setText(String(typeTxt));
typeTv.setTextColor(subTextColor);
typeTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
infoBox.addView(typeTv);
card.addView(infoBox);
// Actions
var actions = new android.widget.LinearLayout(context); var actions = new android.widget.LinearLayout(context);
actions.setOrientation(android.widget.LinearLayout.HORIZONTAL); actions.setOrientation(android.widget.LinearLayout.HORIZONTAL);
actions.setGravity(android.view.Gravity.RIGHT | android.view.Gravity.CENTER_VERTICAL);
if (idx > 0) { actions.setPadding(0, self.dp(9), 0, 0);
actions.addView(self.ui.createFlatButton(self, "↑", subTextColor, function() { if (idx > 0) actions.addView(makeChip("上搬", subTextColor, function() { var tmp = schema[idx]; schema[idx] = schema[idx - 1]; schema[idx - 1] = tmp; refreshPanel(); }));
var temp = schema[idx]; if (idx < schema.length - 1) actions.addView(makeChip("下搬", subTextColor, function() { var tmp = schema[idx]; schema[idx] = schema[idx + 1]; schema[idx + 1] = tmp; refreshPanel(); }));
schema[idx] = schema[idx - 1]; actions.addView(makeChip("编辑", primaryDeep, function() {
schema[idx - 1] = temp; self.state.editingSchemaIndex = idx;
refreshPanel(); clearDraft();
})); if (self.state.toolAppActive && self.pushToolAppPage) self.pushToolAppPage("schema_editor");
} else refreshPanel();
if (idx < schema.length - 1) {
actions.addView(self.ui.createFlatButton(self, "↓", subTextColor, function() {
var temp = schema[idx];
schema[idx] = schema[idx + 1];
schema[idx + 1] = temp;
refreshPanel();
}));
}
actions.addView(self.ui.createFlatButton(self, "✎", C.primary, function() {
self.state.editingSchemaIndex = idx;
if (self.state.toolAppActive && self.pushToolAppPage) self.pushToolAppPage("schema_editor");
else refreshPanel();
})); }));
actions.addView(self.ui.createFlatButton(self, "✕", C.danger, function() { actions.addView(makeChip("移除", dangerColor, function() { schema.splice(idx, 1); refreshPanel(); }));
schema.splice(idx, 1);
refreshPanel();
}));
card.addView(actions); card.addView(actions);
list.addView(card); addWithMargins(list, card, 2, 4, 2, 6);
})(i); })(i);
} }
scroll.addView(list); scroll.addView(list);
panel.addView(scroll); panel.addView(scroll, new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 0, 1));
} else { var bottom = new android.widget.LinearLayout(context);
// Edit Mode bottom.setOrientation(android.widget.LinearLayout.HORIZONTAL);
var editIdx = this.state.editingSchemaIndex; bottom.setGravity(android.view.Gravity.CENTER_VERTICAL);
var editItem = (editIdx === -1) ? { type: "bool", name: "", key: "" } : JSON.parse(JSON.stringify(schema[editIdx])); bottom.setPadding(0, self.dp(8), 0, 0);
var resetBtn = makeChip("恢复默认蓝图", dangerColor, function() {
var btnBack = self.ui.createFlatButton(self, "返回", C.textSecLight, function() { ConfigManager.resetSchema();
self.state.tempSchema = null;
clearDraft();
showToast("已恢复默认蓝图");
refreshPanel();
});
bottom.addView(resetBtn, new android.widget.LinearLayout.LayoutParams(0, self.dp(42), 1));
var saveBtn = self.ui.createSolidButton(self, "保存蓝图", primaryColor, T && T.onPrimary ? T.onPrimary : android.graphics.Color.WHITE, function() {
ConfigManager.saveSchema(schema);
self.state.tempSchema = null;
clearDraft();
showToast("蓝图已保存");
if (self.state.toolAppActive && self.popToolAppPage) {
self.state.editingSchemaIndex = null; self.state.editingSchemaIndex = null;
if (self.state.toolAppActive && self.popToolAppPage) { self.popToolAppPage("schema_save_all");
self.state.keepSchemaEditorState = true; } else {
self.popToolAppPage("schema_edit_back"); self.hideAllPanels();
} else refreshPanel(); self.showPanelAvoidBall("settings");
}
}); });
header.addView(btnBack); var saveLp = new android.widget.LinearLayout.LayoutParams(0, self.dp(42), 1);
panel.addView(header); saveLp.setMargins(self.dp(8), 0, 0, 0);
panel.setTag(header); // 暴露 Header bottom.addView(saveBtn, saveLp);
panel.addView(bottom, lpFullWrap());
var schemaInlineNotice = new android.widget.TextView(context); return panel;
schemaInlineNotice.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
schemaInlineNotice.setPadding(self.dp(12), self.dp(8), self.dp(12), self.dp(8));
schemaInlineNotice.setGravity(android.view.Gravity.CENTER_VERTICAL);
schemaInlineNotice.setVisibility(android.view.View.GONE);
function updateSchemaInlineNotice(msg, kind) {
try {
var k = String(kind || "info");
var color = (k === "error") ? C.error : C.primary;
var bg = (k === "error") ? self.withAlpha(C.error, isDark ? 0.20 : 0.10) : self.withAlpha(C.primary, isDark ? 0.18 : 0.10);
var stroke = (k === "error") ? self.withAlpha(C.error, isDark ? 0.44 : 0.30) : self.withAlpha(C.primary, isDark ? 0.34 : 0.22);
schemaInlineNotice.setText(String(msg || ""));
schemaInlineNotice.setTextColor(color);
schemaInlineNotice.setBackground(self.ui.createStrokeDrawable(bg, stroke, self.dp(1), self.dp(14)));
schemaInlineNotice.setVisibility(android.view.View.VISIBLE);
} catch(eSIN) { safeLog(null, 'e', "catch " + String(eSIN)); }
}
var scroll = new android.widget.ScrollView(context);
var form = new android.widget.LinearLayout(context);
form.setOrientation(android.widget.LinearLayout.VERTICAL);
form.addView(schemaInlineNotice, new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT));
scroll.addView(form);
function createInput(label, key, inputType, hint) {
var box = new android.widget.LinearLayout(context);
box.setOrientation(android.widget.LinearLayout.VERTICAL);
box.setPadding(0, 0, 0, self.dp(12));
var lb = new android.widget.TextView(context);
lb.setText(label);
lb.setTextColor(subTextColor);
lb.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
box.addView(lb);
var et = new android.widget.EditText(context);
et.setText(String(editItem[key] !== undefined ? editItem[key] : ""));
et.setTextColor(textColor);
et.setHint(hint || "");
et.setHintTextColor(self.withAlpha(subTextColor, 0.5));
et.setBackground(self.ui.createRoundDrawable(inputBgColor, self.dp(8)));
et.setPadding(self.dp(12), self.dp(8), self.dp(12), self.dp(8));
if (inputType) et.setInputType(inputType);
box.addView(et);
form.addView(box);
return {
getValue: function() { return String(et.getText()); },
getView: function() { return box; }
};
}
// Type Selector
var typeBox = new android.widget.LinearLayout(context);
typeBox.setOrientation(android.widget.LinearLayout.HORIZONTAL);
typeBox.setGravity(android.view.Gravity.CENTER_VERTICAL);
typeBox.setPadding(0, 0, 0, self.dp(12));
var typeLb = new android.widget.TextView(context);
typeLb.setText("类型: ");
typeLb.setTextColor(textColor);
typeBox.addView(typeLb);
var types = ["section", "bool", "int", "float", "text", "action"];
var typeBtn = self.ui.createSolidButton(self, editItem.type, C.accent, android.graphics.Color.WHITE, function(v) {
var currIdx = types.indexOf(editItem.type);
var nextIdx = (currIdx + 1) % types.length;
editItem.type = types[nextIdx];
refreshPanel();
});
typeBox.addView(typeBtn);
form.addView(typeBox);
var inputName = createInput("显示名称 (Name)", "name", android.text.InputType.TYPE_CLASS_TEXT, "例如:悬浮球大小");
var inputKey = null;
var inputMin = null;
var inputMax = null;
var inputStep = null;
var inputAction = null;
if (editItem.type !== "section") {
inputKey = createInput("配置键名 (Key)", "key", android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS, "例如BALL_SIZE_DP");
}
if (editItem.type === "int" || editItem.type === "float") {
inputMin = createInput("最小值 (Min)", "min", android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED);
inputMax = createInput("最大值 (Max)", "max", android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED);
inputStep = createInput("步进 (Step)", "step", android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL);
}
if (editItem.type === "action") {
inputAction = createInput("动作ID (Action)", "action", android.text.InputType.TYPE_CLASS_TEXT, "例如open_btn_mgr");
}
panel.addView(scroll);
// Save Button
var btnSave = self.ui.createSolidButton(self, "暂存", C.primary, android.graphics.Color.WHITE, function() {
try {
editItem.name = inputName.getValue();
if (inputKey) editItem.key = inputKey.getValue();
if (inputMin) editItem.min = Number(inputMin.getValue());
if (inputMax) editItem.max = Number(inputMax.getValue());
if (inputStep) editItem.step = Number(inputStep.getValue());
if (inputAction) editItem.action = inputAction.getValue();
// Clean up properties based on type
if (editItem.type === "section") { delete editItem.key; delete editItem.min; delete editItem.max; delete editItem.step; delete editItem.action; }
else if (editItem.type === "bool") { delete editItem.min; delete editItem.max; delete editItem.step; delete editItem.action; }
else if (editItem.type === "int" || editItem.type === "float") { delete editItem.action; }
else if (editItem.type === "action") { delete editItem.min; delete editItem.max; delete editItem.step; }
if (editIdx === -1) {
schema.push(editItem);
} else {
schema[editIdx] = editItem;
}
self.state.editingSchemaIndex = null;
if (self.state.toolAppActive && self.popToolAppPage) {
self.state.keepSchemaEditorState = true;
self.popToolAppPage("schema_edit_save");
} else refreshPanel();
} catch (e) {
updateSchemaInlineNotice("暂存失败: " + e, "error");
try { scroll.post(new java.lang.Runnable({ run: function() { try { scroll.fullScroll(android.view.View.FOCUS_UP); } catch(eScrollSchema) {} } })); } catch(ePostSchema) {}
}
});
var bottomBar = new android.widget.LinearLayout(context);
bottomBar.setOrientation(android.widget.LinearLayout.VERTICAL);
bottomBar.setPadding(0, self.dp(8), 0, 0);
bottomBar.addView(btnSave);
panel.addView(bottomBar);
} }
var editIdx = this.state.editingSchemaIndex;
if (this.state.schemaEditingDraftIndex !== editIdx || !this.state.schemaEditingDraft) {
var baseItem = (editIdx === -1) ? { type: "bool", name: "", key: "" } : (schema[editIdx] ? JSON.parse(JSON.stringify(schema[editIdx])) : { type: "bool", name: "", key: "" });
this.state.schemaEditingDraft = baseItem;
this.state.schemaEditingDraftIndex = editIdx;
}
var editItem = this.state.schemaEditingDraft;
if (!editItem.type) editItem.type = "bool";
var formCard = new android.widget.LinearLayout(context);
formCard.setOrientation(android.widget.LinearLayout.VERTICAL);
formCard.setPadding(self.dp(14), self.dp(12), self.dp(14), self.dp(12));
try { formCard.setBackground(self.ui.createStrokeDrawable(cardColor, self.withAlpha(strokeColor, isDark ? 0.32 : 0.42), self.dp(1), self.dp(20))); } catch(eFormBg) {}
var editTitle = makeText(editIdx === -1 ? "添加蓝图项" : "整理蓝图项", 17, textColor, true);
formCard.addView(editTitle);
var editHint = makeText("修改后先“暂存”,回到列表再统一保存蓝图。", 12, subTextColor, false);
editHint.setPadding(0, self.dp(4), 0, self.dp(10));
formCard.addView(editHint);
var schemaInlineNotice = makeText("", 12, primaryDeep, false);
schemaInlineNotice.setPadding(self.dp(12), self.dp(8), self.dp(12), self.dp(8));
schemaInlineNotice.setVisibility(android.view.View.GONE);
function updateSchemaInlineNotice(msg, kind) {
try {
var color = String(kind || "info") === "error" ? dangerColor : primaryDeep;
schemaInlineNotice.setText(String(msg || ""));
schemaInlineNotice.setTextColor(color);
schemaInlineNotice.setBackground(self.ui.createStrokeDrawable(self.withAlpha(color, isDark ? 0.18 : 0.08), self.withAlpha(color, isDark ? 0.42 : 0.25), self.dp(1), self.dp(14)));
schemaInlineNotice.setVisibility(android.view.View.VISIBLE);
} catch(eNotice) {}
}
formCard.addView(schemaInlineNotice, lpFullWrap());
var scroll2 = new android.widget.ScrollView(context);
try { scroll2.setVerticalScrollBarEnabled(false); scroll2.setOverScrollMode(android.view.View.OVER_SCROLL_NEVER); } catch(eScroll2) {}
var form = new android.widget.LinearLayout(context);
form.setOrientation(android.widget.LinearLayout.VERTICAL);
scroll2.addView(form);
function createInput(label, key, inputType, hint) {
var box = new android.widget.LinearLayout(context);
box.setOrientation(android.widget.LinearLayout.VERTICAL);
box.setPadding(0, 0, 0, self.dp(10));
box.addView(makeText(label, 12, subTextColor, false));
var et = new android.widget.EditText(context);
et.setText(String(editItem[key] !== undefined && editItem[key] !== null ? editItem[key] : ""));
et.setTextColor(textColor);
et.setHint(String(hint || ""));
et.setHintTextColor(self.withAlpha(subTextColor, 0.55));
et.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14);
et.setSingleLine(true);
et.setBackground(self.ui.createStrokeDrawable(inputBgColor, self.withAlpha(strokeColor, 0.55), self.dp(1), self.dp(12)));
et.setPadding(self.dp(12), self.dp(7), self.dp(12), self.dp(7));
if (inputType) et.setInputType(inputType);
box.addView(et, lpFullWrap());
form.addView(box, lpFullWrap());
return { input: et, getValue: function() { return String(et.getText()); } };
}
var inputName = null;
var inputKey = null;
var inputMin = null;
var inputMax = null;
var inputStep = null;
var inputAction = null;
function syncDraftFromInputs() {
try {
if (inputName) editItem.name = inputName.getValue();
if (inputKey) editItem.key = inputKey.getValue();
if (inputMin) editItem.min = inputMin.getValue();
if (inputMax) editItem.max = inputMax.getValue();
if (inputStep) editItem.step = inputStep.getValue();
if (inputAction) editItem.action = inputAction.getValue();
self.state.schemaEditingDraft = editItem;
self.state.schemaEditingDraftIndex = editIdx;
} catch(eSync) { safeLog(null, 'e', "schema draft sync err=" + String(eSync)); }
}
var typeRow = new android.widget.LinearLayout(context);
typeRow.setOrientation(android.widget.LinearLayout.HORIZONTAL);
typeRow.setGravity(android.view.Gravity.CENTER_VERTICAL);
typeRow.setPadding(0, 0, 0, self.dp(10));
typeRow.addView(makeText("项目类型", 12, subTextColor, false), new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1));
var types = ["section", "bool", "int", "float", "text", "action"];
var typeNames = { section: "分组标题", bool: "开关", int: "整数", float: "小数", text: "文本", action: "动作入口" };
typeRow.addView(makeChip(typeNames[String(editItem.type)] || String(editItem.type), primaryDeep, function() {
syncDraftFromInputs();
var currIdx = types.indexOf(String(editItem.type || "bool"));
if (currIdx < 0) currIdx = 1;
editItem.type = types[(currIdx + 1) % types.length];
self.state.schemaEditingDraft = editItem;
self.state.schemaEditingDraftIndex = editIdx;
refreshPanel();
}));
form.addView(typeRow, lpFullWrap());
inputName = createInput("显示名字", "name", android.text.InputType.TYPE_CLASS_TEXT, "例如:漂浮气球");
if (editItem.type !== "section") {
inputKey = createInput("配置键", "key", android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS, "例如BALL_SIZE_DP");
}
if (editItem.type === "int" || editItem.type === "float") {
inputMin = createInput("最小值", "min", android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED, "可留空");
inputMax = createInput("最大值", "max", android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED, "可留空");
inputStep = createInput("步进", "step", android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL, "可留空");
}
if (editItem.type === "action") {
inputAction = createInput("动作 ID", "action", android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS, "例如open_btn_mgr");
}
formCard.addView(scroll2, new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 0, 1));
panel.addView(formCard, new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 0, 1));
var editBottom = new android.widget.LinearLayout(context);
editBottom.setOrientation(android.widget.LinearLayout.HORIZONTAL);
editBottom.setGravity(android.view.Gravity.CENTER_VERTICAL);
editBottom.setPadding(0, self.dp(8), 0, 0);
editBottom.addView(makeChip("不改了", subTextColor, function() {
clearDraft();
self.state.editingSchemaIndex = null;
if (self.state.toolAppActive && self.popToolAppPage) {
self.state.keepSchemaEditorState = true;
self.popToolAppPage("schema_edit_back");
} else refreshPanel();
}), new android.widget.LinearLayout.LayoutParams(0, self.dp(42), 1));
var saveDraftBtn = self.ui.createSolidButton(self, "暂存蓝图项", primaryColor, T && T.onPrimary ? T.onPrimary : android.graphics.Color.WHITE, function() {
try {
syncDraftFromInputs();
normalizeItem(editItem);
var err = validateItem(editItem);
if (err) {
updateSchemaInlineNotice(err, "error");
try { scroll2.fullScroll(android.view.View.FOCUS_UP); } catch(eFocusUp) {}
return;
}
if (editIdx === -1) schema.push(JSON.parse(JSON.stringify(editItem)));
else schema[editIdx] = JSON.parse(JSON.stringify(editItem));
clearDraft();
self.state.editingSchemaIndex = null;
if (self.state.toolAppActive && self.popToolAppPage) {
self.state.keepSchemaEditorState = true;
self.popToolAppPage("schema_edit_save");
} else refreshPanel();
} catch(eSave) {
updateSchemaInlineNotice("暂存失败:" + String(eSave), "error");
try { scroll2.fullScroll(android.view.View.FOCUS_UP); } catch(eFocusUp2) {}
}
});
var saveDraftLp = new android.widget.LinearLayout.LayoutParams(0, self.dp(42), 1);
saveDraftLp.setMargins(self.dp(8), 0, 0, 0);
editBottom.addView(saveDraftBtn, saveDraftLp);
panel.addView(editBottom, lpFullWrap());
return panel; return panel;
}; };

View File

@@ -66,8 +66,8 @@
"size": 267207 "size": 267207
}, },
"th_14_schema_editor.js": { "th_14_schema_editor.js": {
"sha256": "798dbd880f22e8efe3d05f522cd0f149384366d22f373c0a13e2c603874c7faf", "sha256": "5669d0b5a16f770bed24eedee24203df57f7cbc7910c840931e533adac1ef146",
"size": 14758 "size": 20484
}, },
"th_15_extra.js": { "th_15_extra.js": {
"sha256": "f49d9e94702ff6b69b800aea10ae2d21dc0d52246ad176a92904a55352dbbf82", "sha256": "f49d9e94702ff6b69b800aea10ae2d21dc0d52246ad176a92904a55352dbbf82",
@@ -80,5 +80,5 @@
}, },
"keyId": "toolhub-targets-2026-rsa3072", "keyId": "toolhub-targets-2026-rsa3072",
"schema": 2, "schema": 2,
"version": 20260522174733 "version": 20260522181206
} }

View File

@@ -1 +1 @@
fiGtRS2KbVOMY9vKGhE3Axv7TD1O2D7sw4DwnwYBjdu6fDkQUDjbbtC8wOElpTRr0uiMI41JBDmi03O5SiXwr0Ua4u3nwdAzS6Uz1F8ZMaBDeW/iQFtTSRLxUm2t5ZsyR/L1BqfpEIx3IwkXX7/yi19qwExdQSfAojkkGXu4c1+e1miTRXNLcOsWniWvDM6BnECTE82ECMHH7w7sc22RaSYkDxLas4yn7uizuoLGfz1no0vzmwuHl3LYEe8UAZ4nETuXvwyxNZKhZ9yHQ8HBuXOYe0HyCpOfA2lxS4GUT2GkByGeU4MYykJqILcLpYYb5UPu8AMAoVmjQjIyy42fzl4VX9nW67ajCys/3wGxDU/r4EqrqII8hVMOkMzE0wxR1zAeHZWJNNjJLv8zD4TEdUh11xpGfTfIsAlWLGbL6dvim8iE3OTjIAAlNGQmNRK9NJRv3zrYzaLjXHvmcl02eWbcVM+hWi2d7pVyzE1YkGz033U/MdvwhCqwY7gJ4tvH Oj/koe1KG25KEOTq/5jJfYFRBPL3ue9kSuhhKc7h0DGGpKJPwt0P1lmLCwJAKpIbwaQiuJo806UKW6r1qtHdjptDU0XreC8URd27lLWg1YgT61mVTKZK7qW8XSV71AebS51sHP+AmqAP9aiIdDRF1atUr0IX4+lxZAcN5t14aDLPwAqd0Kq5YJUUAjGACPbA9JuwjEwyXxlZ2NoyM2KZyDE983VlbJTEYUpvQYGZKKCSpPsowdNdz5OQMouDKigyLbCKQNva3+JtFfX4ZTzz4NMVJesBw82Q2MhlosCoAnrubhwj15MAYEMEP6TpajxjZVGVvFqCLJGh6d4lTq3i1RWPBGq7jTgIZPhlhUXSypt+v8TvWgv5UdmmDE+tSvPdNE+j8oLsah3toquYgiUcWnNaN2qdU9taa3cLb9JBOY1+mgs/M21yjYUC7A4WYpX7N4FPOqGLRUF0tPABBY1L6hY/gJ9YKPvvM4LC6e0C0cZThuBCJu/obk40Dl7iTcpS