From 52b13bea1977eb53326832bdff40878bd8763f34 Mon Sep 17 00:00:00 2001 From: 7015725 Date: Sat, 23 May 2026 01:48:19 +0800 Subject: [PATCH] refactor: split schema editor module --- ToolHub.js | 2 +- ToolHub.js.sha256 | 2 +- code/th_14_panels.js | 325 +-------------------------- code/th_14_schema_editor.js | 327 ++++++++++++++++++++++++++++ manifest.json | 10 +- manifest.sig | 2 +- scripts/generate_signed_manifest.py | 3 +- 7 files changed, 341 insertions(+), 330 deletions(-) create mode 100644 code/th_14_schema_editor.js diff --git a/ToolHub.js b/ToolHub.js index 9b4e57d..35f705c 100644 --- a/ToolHub.js +++ b/ToolHub.js @@ -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", "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_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 loadErrors = []; var criticalModules = { "th_01_base.js": true, "th_16_entry.js": true }; diff --git a/ToolHub.js.sha256 b/ToolHub.js.sha256 index 56a617c..cd7a79b 100644 --- a/ToolHub.js.sha256 +++ b/ToolHub.js.sha256 @@ -1 +1 @@ -1649183178273623b1c72ad228ac0622c3853c9f1ab46f2766574ca097048da7 ToolHub.js +a154dbf74570e6cd8ca14ad212c9213824b9c82d94722e0959e9e51cce577447 ToolHub.js diff --git a/code/th_14_panels.js b/code/th_14_panels.js index 939849e..63b4ac4 100644 --- a/code/th_14_panels.js +++ b/code/th_14_panels.js @@ -4580,329 +4580,8 @@ try { -// =======================【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; -}; +// Schema 编辑器已拆分到 th_14_schema_editor.js。 +// 保留 th_14_panels.js 专注设置/按钮面板与共用 picker 外壳。 diff --git a/code/th_14_schema_editor.js b/code/th_14_schema_editor.js new file mode 100644 index 0000000..dc3fa8b --- /dev/null +++ b/code/th_14_schema_editor.js @@ -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; +}; diff --git a/manifest.json b/manifest.json index 891e3fe..e4e03ca 100644 --- a/manifest.json +++ b/manifest.json @@ -62,8 +62,12 @@ "size": 23906 }, "th_14_panels.js": { - "sha256": "ffa52e1e5c6ce2e53be0c5fe8f65f93385346d0efd9300bebb64f9e258520d61", - "size": 281587 + "sha256": "1af01633720865a83a10a1e5261e3738d91be310195b014e17e579893eae90ee", + "size": 267207 + }, + "th_14_schema_editor.js": { + "sha256": "798dbd880f22e8efe3d05f522cd0f149384366d22f373c0a13e2c603874c7faf", + "size": 14758 }, "th_15_extra.js": { "sha256": "f49d9e94702ff6b69b800aea10ae2d21dc0d52246ad176a92904a55352dbbf82", @@ -76,5 +80,5 @@ }, "keyId": "toolhub-targets-2026-rsa3072", "schema": 2, - "version": 20260522172854 + "version": 20260522174733 } diff --git a/manifest.sig b/manifest.sig index ffab3e6..5327e81 100644 --- a/manifest.sig +++ b/manifest.sig @@ -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 diff --git a/scripts/generate_signed_manifest.py b/scripts/generate_signed_manifest.py index 77b90f9..9a11147 100755 --- a/scripts/generate_signed_manifest.py +++ b/scripts/generate_signed_manifest.py @@ -28,7 +28,8 @@ MODULES = [ "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_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", ]