refactor: split schema editor module
This commit is contained in:
@@ -341,7 +341,7 @@ function loadScript(relPath) {
|
|||||||
var modules = ["th_01_base.js", "th_02_core.js", "th_03_icon.js", "th_04_theme.js", "th_05_persistence.js",
|
var modules = ["th_01_base.js", "th_02_core.js", "th_03_icon.js", "th_04_theme.js", "th_05_persistence.js",
|
||||||
"th_06_icon_parser.js", "th_07_shortcut.js", "th_08_content.js", "th_09_animation.js",
|
"th_06_icon_parser.js", "th_07_shortcut.js", "th_08_content.js", "th_09_animation.js",
|
||||||
"th_10_shell.js", "th_11_action.js", "th_12_rebuild.js", "th_13_panel_ui.js",
|
"th_10_shell.js", "th_11_action.js", "th_12_rebuild.js", "th_13_panel_ui.js",
|
||||||
"th_14_panels.js", "th_14_color_picker.js", "th_14_icon_picker.js", "th_15_extra.js", "th_16_entry.js"];
|
"th_14_panels.js", "th_14_color_picker.js", "th_14_icon_picker.js", "th_14_schema_editor.js", "th_15_extra.js", "th_16_entry.js"];
|
||||||
var __moduleUpdates = [];
|
var __moduleUpdates = [];
|
||||||
var loadErrors = [];
|
var loadErrors = [];
|
||||||
var criticalModules = { "th_01_base.js": true, "th_16_entry.js": true };
|
var criticalModules = { "th_01_base.js": true, "th_16_entry.js": true };
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1649183178273623b1c72ad228ac0622c3853c9f1ab46f2766574ca097048da7 ToolHub.js
|
a154dbf74570e6cd8ca14ad212c9213824b9c82d94722e0959e9e51cce577447 ToolHub.js
|
||||||
|
|||||||
@@ -4580,329 +4580,8 @@ try {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =======================【Schema 编辑面板】======================
|
// Schema 编辑器已拆分到 th_14_schema_editor.js。
|
||||||
FloatBallAppWM.prototype.buildSchemaEditorPanelView = function() {
|
// 保留 th_14_panels.js 专注设置/按钮面板与共用 picker 外壳。
|
||||||
var self = this;
|
|
||||||
if (this.state.editingSchemaIndex === undefined) {
|
|
||||||
this.state.editingSchemaIndex = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.state.keepSchemaEditorState || !this.state.tempSchema) {
|
|
||||||
var current = ConfigManager.loadSchema();
|
|
||||||
this.state.tempSchema = JSON.parse(JSON.stringify(current));
|
|
||||||
}
|
|
||||||
this.state.keepSchemaEditorState = false;
|
|
||||||
|
|
||||||
var schema = this.state.tempSchema;
|
|
||||||
var isEditing = (this.state.editingSchemaIndex !== null);
|
|
||||||
var isDark = this.isDarkTheme();
|
|
||||||
var C = this.ui.colors;
|
|
||||||
|
|
||||||
var bgColor = isDark ? C.bgDark : C.bgLight;
|
|
||||||
var cardColor = isDark ? C.cardDark : C.cardLight;
|
|
||||||
var textColor = isDark ? C.textPriDark : C.textPriLight;
|
|
||||||
var subTextColor = isDark ? C.textSecDark : C.textSecLight;
|
|
||||||
var dividerColor = isDark ? C.dividerDark : C.dividerLight;
|
|
||||||
var inputBgColor = isDark ? C.inputBgDark : C.inputBgLight;
|
|
||||||
|
|
||||||
var panel = this.ui.createStyledPanel(this, 16);
|
|
||||||
|
|
||||||
// --- Header ---
|
|
||||||
var header = this.ui.createStyledHeader(this, 12);
|
|
||||||
|
|
||||||
// Title removed to avoid redundancy with Wrapper Title
|
|
||||||
// var titleTv = new android.widget.TextView(context);
|
|
||||||
// titleTv.setText(isEditing ? (this.state.editingSchemaIndex === -1 ? "新增项" : "编辑项") : "设置布局管理");
|
|
||||||
// ...
|
|
||||||
// header.addView(titleTv);
|
|
||||||
|
|
||||||
// Placeholder to push buttons to right
|
|
||||||
header.addView(this.ui.createSpacer(this));
|
|
||||||
|
|
||||||
function refreshPanel() {
|
|
||||||
self.state.keepSchemaEditorState = true;
|
|
||||||
self.showPanelAvoidBall("schema_editor");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isEditing) {
|
|
||||||
// List Mode
|
|
||||||
header.addView(self.ui.createFlatButton(self, "重置", C.danger, function() {
|
|
||||||
ConfigManager.resetSchema();
|
|
||||||
self.state.tempSchema = null;
|
|
||||||
self.toast("已重置为默认布局");
|
|
||||||
refreshPanel();
|
|
||||||
}));
|
|
||||||
|
|
||||||
header.addView(self.ui.createFlatButton(self, "新增", C.primary, function() {
|
|
||||||
self.state.editingSchemaIndex = -1;
|
|
||||||
if (self.state.toolAppActive && self.pushToolAppPage) self.pushToolAppPage("schema_editor");
|
|
||||||
else refreshPanel();
|
|
||||||
}));
|
|
||||||
|
|
||||||
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);
|
|
||||||
try { scroll.setVerticalScrollBarEnabled(false); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
|
|
||||||
var list = new android.widget.LinearLayout(context);
|
|
||||||
list.setOrientation(android.widget.LinearLayout.VERTICAL);
|
|
||||||
list.setPadding(0, self.dp(4), 0, self.dp(4));
|
|
||||||
|
|
||||||
for (var i = 0; i < schema.length; i++) {
|
|
||||||
(function(idx) {
|
|
||||||
var item = schema[idx];
|
|
||||||
|
|
||||||
var card = new android.widget.LinearLayout(context);
|
|
||||||
card.setOrientation(android.widget.LinearLayout.HORIZONTAL);
|
|
||||||
card.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
|
||||||
card.setBackground(self.ui.createRoundDrawable(cardColor, self.dp(8)));
|
|
||||||
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);
|
|
||||||
cardLp.setMargins(self.dp(2), self.dp(4), self.dp(2), self.dp(4));
|
|
||||||
card.setLayoutParams(cardLp);
|
|
||||||
card.setPadding(self.dp(12), self.dp(12), self.dp(8), self.dp(12));
|
|
||||||
|
|
||||||
// Info
|
|
||||||
var infoBox = new android.widget.LinearLayout(context);
|
|
||||||
infoBox.setOrientation(android.widget.LinearLayout.VERTICAL);
|
|
||||||
var infoLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
infoLp.weight = 1;
|
|
||||||
infoBox.setLayoutParams(infoLp);
|
|
||||||
|
|
||||||
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);
|
|
||||||
actions.setOrientation(android.widget.LinearLayout.HORIZONTAL);
|
|
||||||
|
|
||||||
if (idx > 0) {
|
|
||||||
actions.addView(self.ui.createFlatButton(self, "↑", subTextColor, function() {
|
|
||||||
var temp = schema[idx];
|
|
||||||
schema[idx] = schema[idx - 1];
|
|
||||||
schema[idx - 1] = temp;
|
|
||||||
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() {
|
|
||||||
schema.splice(idx, 1);
|
|
||||||
refreshPanel();
|
|
||||||
}));
|
|
||||||
|
|
||||||
card.addView(actions);
|
|
||||||
list.addView(card);
|
|
||||||
})(i);
|
|
||||||
}
|
|
||||||
scroll.addView(list);
|
|
||||||
panel.addView(scroll);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Edit Mode
|
|
||||||
var editIdx = this.state.editingSchemaIndex;
|
|
||||||
var editItem = (editIdx === -1) ? { type: "bool", name: "", key: "" } : JSON.parse(JSON.stringify(schema[editIdx]));
|
|
||||||
|
|
||||||
var btnBack = self.ui.createFlatButton(self, "返回", C.textSecLight, function() {
|
|
||||||
self.state.editingSchemaIndex = null;
|
|
||||||
if (self.state.toolAppActive && self.popToolAppPage) {
|
|
||||||
self.state.keepSchemaEditorState = true;
|
|
||||||
self.popToolAppPage("schema_edit_back");
|
|
||||||
} else refreshPanel();
|
|
||||||
});
|
|
||||||
header.addView(btnBack);
|
|
||||||
panel.addView(header);
|
|
||||||
panel.setTag(header); // 暴露 Header
|
|
||||||
|
|
||||||
var schemaInlineNotice = new android.widget.TextView(context);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return panel;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
327
code/th_14_schema_editor.js
Normal file
327
code/th_14_schema_editor.js
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
// ToolHub - Schema 编辑器模块
|
||||||
|
// 依赖:th_14_panels.js 中的设置页基础 UI / showPanelAvoidBall,th_05_persistence.js 的 ConfigManager。
|
||||||
|
// 注意:加载顺序必须位于 th_14_panels.js 之后、th_15_extra.js 之前。
|
||||||
|
|
||||||
|
// =======================【Schema 编辑面板】======================
|
||||||
|
FloatBallAppWM.prototype.buildSchemaEditorPanelView = function() {
|
||||||
|
var self = this;
|
||||||
|
if (this.state.editingSchemaIndex === undefined) {
|
||||||
|
this.state.editingSchemaIndex = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.state.keepSchemaEditorState || !this.state.tempSchema) {
|
||||||
|
var current = ConfigManager.loadSchema();
|
||||||
|
this.state.tempSchema = JSON.parse(JSON.stringify(current));
|
||||||
|
}
|
||||||
|
this.state.keepSchemaEditorState = false;
|
||||||
|
|
||||||
|
var schema = this.state.tempSchema;
|
||||||
|
var isEditing = (this.state.editingSchemaIndex !== null);
|
||||||
|
var isDark = this.isDarkTheme();
|
||||||
|
var C = this.ui.colors;
|
||||||
|
|
||||||
|
var bgColor = isDark ? C.bgDark : C.bgLight;
|
||||||
|
var cardColor = isDark ? C.cardDark : C.cardLight;
|
||||||
|
var textColor = isDark ? C.textPriDark : C.textPriLight;
|
||||||
|
var subTextColor = isDark ? C.textSecDark : C.textSecLight;
|
||||||
|
var dividerColor = isDark ? C.dividerDark : C.dividerLight;
|
||||||
|
var inputBgColor = isDark ? C.inputBgDark : C.inputBgLight;
|
||||||
|
|
||||||
|
var panel = this.ui.createStyledPanel(this, 16);
|
||||||
|
|
||||||
|
// --- Header ---
|
||||||
|
var header = this.ui.createStyledHeader(this, 12);
|
||||||
|
|
||||||
|
// Title removed to avoid redundancy with Wrapper Title
|
||||||
|
// var titleTv = new android.widget.TextView(context);
|
||||||
|
// titleTv.setText(isEditing ? (this.state.editingSchemaIndex === -1 ? "新增项" : "编辑项") : "设置布局管理");
|
||||||
|
// ...
|
||||||
|
// header.addView(titleTv);
|
||||||
|
|
||||||
|
// Placeholder to push buttons to right
|
||||||
|
header.addView(this.ui.createSpacer(this));
|
||||||
|
|
||||||
|
function refreshPanel() {
|
||||||
|
self.state.keepSchemaEditorState = true;
|
||||||
|
self.showPanelAvoidBall("schema_editor");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isEditing) {
|
||||||
|
// List Mode
|
||||||
|
header.addView(self.ui.createFlatButton(self, "重置", C.danger, function() {
|
||||||
|
ConfigManager.resetSchema();
|
||||||
|
self.state.tempSchema = null;
|
||||||
|
self.toast("已重置为默认布局");
|
||||||
|
refreshPanel();
|
||||||
|
}));
|
||||||
|
|
||||||
|
header.addView(self.ui.createFlatButton(self, "新增", C.primary, function() {
|
||||||
|
self.state.editingSchemaIndex = -1;
|
||||||
|
if (self.state.toolAppActive && self.pushToolAppPage) self.pushToolAppPage("schema_editor");
|
||||||
|
else refreshPanel();
|
||||||
|
}));
|
||||||
|
|
||||||
|
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);
|
||||||
|
try { scroll.setVerticalScrollBarEnabled(false); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
|
||||||
|
var list = new android.widget.LinearLayout(context);
|
||||||
|
list.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||||
|
list.setPadding(0, self.dp(4), 0, self.dp(4));
|
||||||
|
|
||||||
|
for (var i = 0; i < schema.length; i++) {
|
||||||
|
(function(idx) {
|
||||||
|
var item = schema[idx];
|
||||||
|
|
||||||
|
var card = new android.widget.LinearLayout(context);
|
||||||
|
card.setOrientation(android.widget.LinearLayout.HORIZONTAL);
|
||||||
|
card.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||||
|
card.setBackground(self.ui.createRoundDrawable(cardColor, self.dp(8)));
|
||||||
|
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);
|
||||||
|
cardLp.setMargins(self.dp(2), self.dp(4), self.dp(2), self.dp(4));
|
||||||
|
card.setLayoutParams(cardLp);
|
||||||
|
card.setPadding(self.dp(12), self.dp(12), self.dp(8), self.dp(12));
|
||||||
|
|
||||||
|
// Info
|
||||||
|
var infoBox = new android.widget.LinearLayout(context);
|
||||||
|
infoBox.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||||
|
var infoLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
infoLp.weight = 1;
|
||||||
|
infoBox.setLayoutParams(infoLp);
|
||||||
|
|
||||||
|
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);
|
||||||
|
actions.setOrientation(android.widget.LinearLayout.HORIZONTAL);
|
||||||
|
|
||||||
|
if (idx > 0) {
|
||||||
|
actions.addView(self.ui.createFlatButton(self, "↑", subTextColor, function() {
|
||||||
|
var temp = schema[idx];
|
||||||
|
schema[idx] = schema[idx - 1];
|
||||||
|
schema[idx - 1] = temp;
|
||||||
|
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() {
|
||||||
|
schema.splice(idx, 1);
|
||||||
|
refreshPanel();
|
||||||
|
}));
|
||||||
|
|
||||||
|
card.addView(actions);
|
||||||
|
list.addView(card);
|
||||||
|
})(i);
|
||||||
|
}
|
||||||
|
scroll.addView(list);
|
||||||
|
panel.addView(scroll);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Edit Mode
|
||||||
|
var editIdx = this.state.editingSchemaIndex;
|
||||||
|
var editItem = (editIdx === -1) ? { type: "bool", name: "", key: "" } : JSON.parse(JSON.stringify(schema[editIdx]));
|
||||||
|
|
||||||
|
var btnBack = self.ui.createFlatButton(self, "返回", C.textSecLight, function() {
|
||||||
|
self.state.editingSchemaIndex = null;
|
||||||
|
if (self.state.toolAppActive && self.popToolAppPage) {
|
||||||
|
self.state.keepSchemaEditorState = true;
|
||||||
|
self.popToolAppPage("schema_edit_back");
|
||||||
|
} else refreshPanel();
|
||||||
|
});
|
||||||
|
header.addView(btnBack);
|
||||||
|
panel.addView(header);
|
||||||
|
panel.setTag(header); // 暴露 Header
|
||||||
|
|
||||||
|
var schemaInlineNotice = new android.widget.TextView(context);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
};
|
||||||
@@ -62,8 +62,12 @@
|
|||||||
"size": 23906
|
"size": 23906
|
||||||
},
|
},
|
||||||
"th_14_panels.js": {
|
"th_14_panels.js": {
|
||||||
"sha256": "ffa52e1e5c6ce2e53be0c5fe8f65f93385346d0efd9300bebb64f9e258520d61",
|
"sha256": "1af01633720865a83a10a1e5261e3738d91be310195b014e17e579893eae90ee",
|
||||||
"size": 281587
|
"size": 267207
|
||||||
|
},
|
||||||
|
"th_14_schema_editor.js": {
|
||||||
|
"sha256": "798dbd880f22e8efe3d05f522cd0f149384366d22f373c0a13e2c603874c7faf",
|
||||||
|
"size": 14758
|
||||||
},
|
},
|
||||||
"th_15_extra.js": {
|
"th_15_extra.js": {
|
||||||
"sha256": "f49d9e94702ff6b69b800aea10ae2d21dc0d52246ad176a92904a55352dbbf82",
|
"sha256": "f49d9e94702ff6b69b800aea10ae2d21dc0d52246ad176a92904a55352dbbf82",
|
||||||
@@ -76,5 +80,5 @@
|
|||||||
},
|
},
|
||||||
"keyId": "toolhub-targets-2026-rsa3072",
|
"keyId": "toolhub-targets-2026-rsa3072",
|
||||||
"schema": 2,
|
"schema": 2,
|
||||||
"version": 20260522172854
|
"version": 20260522174733
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
pJhjeC2ap21SfgKvJoFteAz3fKftsGUtj1bthDccyVjP/IUVYA9kcCx/7L8cCJiID4vmLdCWhbPs+36vLX+61o+i8kxM2Ua326KbEnfaYM5UCWX/X9p/ktCs2vtr2surfYTBOpXlvieY92DHbizYY2ICMCF81FvxREUuKeNfMeTrF1QOAa0u7RJwMuPc1S6EVgtd07J6pkO6xvcEpcPIQ2x/sZVP4/kwCyhVvwNS58J2AIzHqN2umyFgoSO4Cxyizn72iI1HHQwnZ+fsNARj/SDnFbnfIEWGOW405dwuU89nGLQfvMMU/cvinvuVIhTv1QhKzQgdhNaJpPGoqmicIqdAR7bSem41Ut2DR+4bc9Le8axtS9GL8yexydOeym14KqVXWMYDfhTkXuCIyOBrZzJeJprXxzK4UkFv5WWPV6AYEpKhxBhPN8NOvlGUtVLwsfUzE5C06QogLhKiWYh6WJT9JmAuOZtkFyKXGjNj3lBZq6TOVLZG3KDjVhKpLcla
|
fiGtRS2KbVOMY9vKGhE3Axv7TD1O2D7sw4DwnwYBjdu6fDkQUDjbbtC8wOElpTRr0uiMI41JBDmi03O5SiXwr0Ua4u3nwdAzS6Uz1F8ZMaBDeW/iQFtTSRLxUm2t5ZsyR/L1BqfpEIx3IwkXX7/yi19qwExdQSfAojkkGXu4c1+e1miTRXNLcOsWniWvDM6BnECTE82ECMHH7w7sc22RaSYkDxLas4yn7uizuoLGfz1no0vzmwuHl3LYEe8UAZ4nETuXvwyxNZKhZ9yHQ8HBuXOYe0HyCpOfA2lxS4GUT2GkByGeU4MYykJqILcLpYYb5UPu8AMAoVmjQjIyy42fzl4VX9nW67ajCys/3wGxDU/r4EqrqII8hVMOkMzE0wxR1zAeHZWJNNjJLv8zD4TEdUh11xpGfTfIsAlWLGbL6dvim8iE3OTjIAAlNGQmNRK9NJRv3zrYzaLjXHvmcl02eWbcVM+hWi2d7pVyzE1YkGz033U/MdvwhCqwY7gJ4tvH
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ MODULES = [
|
|||||||
"th_05_persistence.js", "th_06_icon_parser.js", "th_07_shortcut.js",
|
"th_05_persistence.js", "th_06_icon_parser.js", "th_07_shortcut.js",
|
||||||
"th_08_content.js", "th_09_animation.js", "th_10_shell.js",
|
"th_08_content.js", "th_09_animation.js", "th_10_shell.js",
|
||||||
"th_11_action.js", "th_12_rebuild.js", "th_13_panel_ui.js",
|
"th_11_action.js", "th_12_rebuild.js", "th_13_panel_ui.js",
|
||||||
"th_14_panels.js", "th_14_color_picker.js", "th_14_icon_picker.js", "th_15_extra.js", "th_16_entry.js",
|
"th_14_panels.js", "th_14_color_picker.js", "th_14_icon_picker.js",
|
||||||
|
"th_14_schema_editor.js", "th_15_extra.js", "th_16_entry.js",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user