From 3b6830e0ecb60d0dbf2b65495e8ce7303c4cb7b6 Mon Sep 17 00:00:00 2001 From: 7015725 Date: Wed, 13 May 2026 07:38:48 +0800 Subject: [PATCH] style: polish button manager and editor layout --- code/th_14_panels.js | 154 +++++++++++++++++++++---- manifest.json | 6 +- manifest.sig | 2 +- scripts/verify_button_editor_layout.py | 23 ++++ 4 files changed, 159 insertions(+), 26 deletions(-) create mode 100644 scripts/verify_button_editor_layout.py diff --git a/code/th_14_panels.js b/code/th_14_panels.js index c7509e5..ec3ab1e 100644 --- a/code/th_14_panels.js +++ b/code/th_14_panels.js @@ -533,6 +533,100 @@ FloatBallAppWM.prototype.createButtonManagerSummaryCard = function(parent, total parent.addView(card, lp); }; +FloatBallAppWM.prototype.createButtonManagerActionChip = function(text, textColor, strokeColor, onClickFn) { + var tv = new android.widget.TextView(context); + tv.setText(String(text || "")); + tv.setGravity(android.view.Gravity.CENTER); + tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); + tv.setTypeface(null, android.graphics.Typeface.BOLD); + tv.setTextColor(textColor); + tv.setMinHeight(this.dp(34)); + tv.setPadding(this.dp(10), 0, this.dp(10), 0); + try { + var bg = new android.graphics.drawable.GradientDrawable(); + bg.setColor(android.graphics.Color.TRANSPARENT); + bg.setCornerRadius(this.dp(12)); + bg.setStroke(this.dp(1), strokeColor); + tv.setBackground(bg); + } catch(eBg) { safeLog(null, 'e', "catch " + String(eBg)); } + if (onClickFn) { + tv.setOnClickListener(new android.view.View.OnClickListener({ onClick: function() { + try { onClickFn(); } catch(eClick) { safeLog(null, 'e', "catch " + String(eClick)); } + }})); + } + return tv; +}; + +FloatBallAppWM.prototype.createButtonManagerPolishedCard = function(parent, title, subtitle, statText) { + var isDark = this.isDarkTheme(); + var C = this.ui.colors; + var card = new android.widget.LinearLayout(context); + card.setOrientation(android.widget.LinearLayout.VERTICAL); + card.setPadding(this.dp(14), this.dp(12), this.dp(14), this.dp(12)); + card.setBackground(this.ui.createRoundDrawable(isDark ? this.withAlpha(C.cardDark, 0.74) : this.withAlpha(C.cardLight, 0.78), this.dp(18))); + try { card.setElevation(this.dp(2)); } catch(eElev) {} + var tv = new android.widget.TextView(context); + tv.setText(String(title || "")); + tv.setTextColor(isDark ? C.textPriDark : C.textPriLight); + tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15); + tv.setTypeface(null, android.graphics.Typeface.BOLD); + card.addView(tv); + var sub = new android.widget.TextView(context); + sub.setText(String(subtitle || "")); + sub.setTextColor(isDark ? C.textSecDark : C.textSecLight); + sub.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); + sub.setPadding(0, this.dp(4), 0, 0); + card.addView(sub); + if (statText) { + var chip = this.createButtonManagerActionChip(String(statText), C.primary, this.withAlpha(C.primary, 0.32), null); + var chipLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + chipLp.setMargins(0, this.dp(10), 0, 0); + card.addView(chip, chipLp); + } + var lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + lp.setMargins(this.dp(2), this.dp(2), this.dp(2), this.dp(8)); + parent.addView(card, lp); + return card; +}; + +FloatBallAppWM.prototype.createButtonEditorHeroCard = function(parent, modeText, subtitle) { + var isDark = this.isDarkTheme(); + var C = this.ui.colors; + var card = new android.widget.LinearLayout(context); + card.setOrientation(android.widget.LinearLayout.VERTICAL); + card.setPadding(this.dp(14), this.dp(12), this.dp(14), this.dp(12)); + card.setBackground(this.ui.createRoundDrawable(isDark ? this.withAlpha(C.cardDark, 0.70) : this.withAlpha(C.cardLight, 0.72), this.dp(18))); + try { card.setElevation(this.dp(2)); } catch(eElev) {} + var title = new android.widget.TextView(context); + title.setText("按钮编辑工作台"); + title.setTextColor(isDark ? C.textPriDark : C.textPriLight); + title.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 16); + title.setTypeface(null, android.graphics.Typeface.BOLD); + card.addView(title); + var sub = new android.widget.TextView(context); + sub.setText(String(subtitle || "常用内容默认展开,低频配置可折叠")); + sub.setTextColor(isDark ? C.textSecDark : C.textSecLight); + sub.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); + sub.setPadding(0, this.dp(4), 0, this.dp(8)); + card.addView(sub); + var chip = this.createButtonManagerActionChip(String(modeText || "编辑按钮"), C.primary, this.withAlpha(C.primary, 0.32), null); + card.addView(chip); + var lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + lp.setMargins(this.dp(2), this.dp(2), this.dp(2), this.dp(10)); + parent.addView(card, lp); + return card; +}; + +FloatBallAppWM.prototype.addButtonEditorField = function(parent, view) { + try { + var lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + lp.setMargins(0, this.dp(4), 0, this.dp(8)); + parent.addView(view, lp); + } catch (e) { + try { parent.addView(view); } catch(e2) {} + } +}; + FloatBallAppWM.prototype.createButtonEditorSectionCard = function(parent, title, desc) { var isDark = this.isDarkTheme(); var C = this.ui.colors; @@ -714,29 +808,40 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { else enabledCount++; } } catch(eCnt) { safeLog(null, 'e', "catch " + String(eCnt)); } - self.createButtonManagerSummaryCard(panel, buttons.length, enabledCount, disabledCount); + self.createButtonManagerPolishedCard(panel, "按钮管理首页", "点卡片编辑;下方操作区负责排序、启用和删除", "共 " + buttons.length + " 个 · 已启用 " + enabledCount + " · 已禁用 " + disabledCount); + // 按钮管理搜索卡片:搜索输入与操作按钮收在同一张卡片里,避免顶部显得零散。 + var searchSurface = new android.widget.LinearLayout(context); + searchSurface.setOrientation(android.widget.LinearLayout.VERTICAL); + searchSurface.setPadding(self.dp(12), self.dp(10), self.dp(12), self.dp(10)); + searchSurface.setBackground(self.ui.createRoundDrawable(isDark ? self.withAlpha(C.cardDark, 0.58) : self.withAlpha(C.cardLight, 0.62), self.dp(16))); var searchRow = new android.widget.LinearLayout(context); searchRow.setOrientation(android.widget.LinearLayout.HORIZONTAL); searchRow.setGravity(android.view.Gravity.CENTER_VERTICAL); - searchRow.setPadding(0, 0, 0, self.dp(8)); var searchInput = self.ui.createInputGroup(self, "搜索按钮", self.state.buttonManagerQuery || "", false, "标题 / 类型 / 包名 / 命令"); var searchInputLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); searchInputLp.weight = 1; searchRow.addView(searchInput.view, searchInputLp); - var btnSearch = self.ui.createFlatButton(self, "搜索", C.primary, function() { + var btnSearch = self.createButtonManagerActionChip("搜索", C.primary, self.withAlpha(C.primary, 0.32), function() { try { self.state.buttonManagerQuery = String(searchInput.getValue ? searchInput.getValue() : ""); } catch(eQ) { self.state.buttonManagerQuery = ""; } self.state.btnEditorListScrollY = 0; refreshPanel(); }); - searchRow.addView(btnSearch); - var btnClearSearch = self.ui.createFlatButton(self, "清空", subTextColor, function() { + var btnSearchLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + btnSearchLp.leftMargin = self.dp(8); + searchRow.addView(btnSearch, btnSearchLp); + var btnClearSearch = self.createButtonManagerActionChip("清空", subTextColor, self.withAlpha(subTextColor, 0.24), function() { self.state.buttonManagerQuery = ""; self.state.btnEditorListScrollY = 0; refreshPanel(); }); - searchRow.addView(btnClearSearch); - panel.addView(searchRow); + var btnClearLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + btnClearLp.leftMargin = self.dp(6); + searchRow.addView(btnClearSearch, btnClearLp); + searchSurface.addView(searchRow); + var searchSurfaceLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + searchSurfaceLp.setMargins(self.dp(2), 0, self.dp(2), self.dp(8)); + panel.addView(searchSurface, searchSurfaceLp); var activeQuery = String(self.state.buttonManagerQuery || ""); var visibleCount = 0; @@ -759,9 +864,9 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { var __enabled = true; try { __enabled = (btnCfg && btnCfg.enabled === false) ? false : true; } catch(eEn) { __enabled = true; } - // 卡片容器 + // 按钮管理列表卡片:上信息、下操作;避免图标、标题、排序、删除全部挤在同一条线上。 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); // 使用稍微不同的背景色以突出卡片 var cardBgColor = isDark ? self.withAlpha(C.cardDark, 0.8) : self.withAlpha(C.cardLight, 0.8); @@ -771,7 +876,12 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { var cardLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); cardLp.setMargins(self.dp(4), self.dp(4), self.dp(4), self.dp(4)); card.setLayoutParams(cardLp); - card.setPadding(self.dp(14), self.dp(10), self.dp(10), self.dp(10)); // # 行高优化:降低上下内边距,避免"按钮管理列表"单行过高 + card.setPadding(self.dp(14), self.dp(10), self.dp(10), self.dp(10)); + + var mainRow = new android.widget.LinearLayout(context); + mainRow.setOrientation(android.widget.LinearLayout.HORIZONTAL); + mainRow.setGravity(android.view.Gravity.CENTER_VERTICAL); + mainRow.setPadding(0, 0, 0, self.dp(8)); // # 视觉提示:禁用项整体变淡,方便一眼区分 if (!__enabled) { @@ -805,7 +915,7 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { var iconLp = new android.widget.LinearLayout.LayoutParams(self.dp(iconSizeDp), self.dp(iconSizeDp)); iconLp.rightMargin = self.dp(10); iconIv.setLayoutParams(iconLp); - card.addView(iconIv); + mainRow.addView(iconIv); // 中间文本信息 var textContainer = new android.widget.LinearLayout(context); @@ -836,12 +946,13 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { detailTv.setEllipsize(android.text.TextUtils.TruncateAt.END); textContainer.addView(detailTv); - card.addView(textContainer); + mainRow.addView(textContainer); // 右侧操作区 var actions = new android.widget.LinearLayout(context); actions.setOrientation(android.widget.LinearLayout.HORIZONTAL); - actions.setGravity(android.view.Gravity.CENTER_VERTICAL); // 上移/下移(排序按钮布局优化:扩大点击面积,顶部/底部也占位,避免难点) + actions.setGravity(android.view.Gravity.CENTER_VERTICAL); + actions.setPadding(self.dp(42), 0, 0, 0); // 上移/下移(排序按钮布局优化:扩大点击面积,顶部/底部也占位,避免难点) // 说明:原先 ▲▼ 太小且只有可用时才出现,手指很难点中;这里改为固定 2 个大按钮(44dp),不可用则置灰禁用。 var sortBox = new android.widget.LinearLayout(context); sortBox.setOrientation(android.widget.LinearLayout.HORIZONTAL); // # 排序按钮改为横向排列,减少占高 @@ -963,6 +1074,7 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { })); actions.addView(btnDel); + card.addView(mainRow); card.addView(actions); list.addView(card); @@ -1068,15 +1180,13 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { form.setOrientation(android.widget.LinearLayout.VERTICAL); form.setPadding(self.dp(4), self.dp(4), self.dp(4), self.dp(4)); - // 操作提示 - var editHint = new android.widget.TextView(context); - editHint.setText("提示:选择动作类型并填写相应参数,完成后点击底部暂存"); - editHint.setTextColor(subTextColor); - editHint.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); - editHint.setPadding(self.dp(4), 0, 0, self.dp(16)); - form.addView(editHint); + // 顶部说明卡:比散落的一行提示更清晰,强调一页式编辑的操作路径。 + self.createButtonEditorHeroCard(form, + (editIdx === -1 ? "新增按钮" : "编辑按钮"), + "常用内容默认展开,低频配置可折叠;底部统一暂存。" + ); - var basicSectionBody = self.createButtonEditorCollapsibleSection(form, "基础信息", "按钮名称与基础标识", true); + var basicSectionBody = self.createButtonEditorCollapsibleSection(form, "基础信息", "先填写名称,便于在按钮管理列表中识别", true); // 1. 标题 (Title) var topArea = new android.widget.LinearLayout(context); @@ -1092,7 +1202,7 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { titleArea.setLayoutParams(titleLp); var inputTitle = self.ui.createInputGroup(self, "标题 (Title)", targetBtn.title, false, "按钮上显示的文字"); - titleArea.addView(inputTitle.view); + self.addButtonEditorField(titleArea, inputTitle.view); topArea.addView(titleArea); basicSectionBody.addView(topArea); diff --git a/manifest.json b/manifest.json index 04f7b54..cd61a12 100644 --- a/manifest.json +++ b/manifest.json @@ -54,8 +54,8 @@ "size": 20386 }, "th_14_panels.js": { - "sha256": "1bb02639a6700605af1e98e63ea6fb55a46d46b85822e15178f5a861437e7661", - "size": 237123 + "sha256": "f3aaae49c54a7d5187d83c7408649c8206dac3c461a91bcd870a463a10b8a55f", + "size": 243664 }, "th_15_extra.js": { "sha256": "44d19f0012f4182b9f9831d4f5a747b43d3b726f98e0480e6c79f54eeff70a5e", @@ -68,5 +68,5 @@ }, "keyId": "toolhub-targets-2026-rsa3072", "schema": 2, - "version": 20260512233123 + "version": 20260512233842 } diff --git a/manifest.sig b/manifest.sig index c38d9f5..59da26d 100644 --- a/manifest.sig +++ b/manifest.sig @@ -1 +1 @@ -eHDpnB+6LIjkd8UjiiNeI6CUb0vK01Dc+HGNJRLJBbjiRCLMTHYiDulV9qDmMDrNPJy4fKr8Wmdz6Lz0AMkv9cbWEut+Fb7jZJXzwIt967XJsKwyQMdPb/N7wZ5oLvdrDxJXbp833FKlnsNahhcrg92Jw3z5qRgBYJQ5JV4CKPkMAXG6w80Y1d7LUhbJsBUtZwGOA2qMykefFVl1qEO67Ljm0pyeqaFqTKcTmmIn955Ni4f8q/gaaR+mz66WhKy/YUhXIYwXSIsW4eF9kkDe3N2NW77goDNKAn4E1Hg9qsObiWxb452zVwhdR/S8J3nk7vLO6N/FVf0+PxT24W5G+S/BDChTz3fwOsS2DUI2ZkygFn9leF/z8Nmivf2utwkgrZbJ/xkSirvYxZ2KzwRY7VSLM7ESPBBpx52EVK6HlVuZL7v+rlr/Yb+lzXvhgtg9s4btLJH9GGtdJVPGItLEBPYd9fi8J+Xupd6nGDHFVMZqULXoK+3a96FqsQr2pqKB +NJfls68utJFK3pZ/wxio4O0buAiyM4oZMpTS5b5JvNUQ3GNW1pEopJeu9sLvZWHxql7tmg/OjTrCVXB+JhwmeRILXcsFLLHp9ogvnHzqchiCUSuwilBWhB07r5fiIq23egUjNs25Z7nRtvx47WCp0lvM9YdMcHPNifPr82VpC9EtTD29Qw+gY+jjPm+GiScY88SvUnpZOVEf/XCh9tBeSz1YrIA+6D6TpfZrRY6dh4GFgLezKd5DNljqw3ig61to6wu0xJcz+v+l9PWq6zoMkTG7XUIP4AGVyKvzkDr7EM8wMjwUq5z6OQiwqp9yBaRx+Av7aGG9IwWz3NxFuYA86Y9z7L2t4e+Ajf1BMYC1vuThH0sG0lLERPWx4VjncOgVx2fthrmrGstF+BkqZW68rmscOIAKQ1Lxz0ySkDfhtKsaXk+xe43AQ1l9wAPIB7pk08TZPaSGY2zXE0/blUvr1TzrgXwK5wWRib0kZlEpOJTRbcItPt8iaAWAZixC0jwC diff --git a/scripts/verify_button_editor_layout.py b/scripts/verify_button_editor_layout.py new file mode 100644 index 0000000..0a074a1 --- /dev/null +++ b/scripts/verify_button_editor_layout.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +import pathlib +import sys + +root = pathlib.Path(__file__).resolve().parents[1] +text = (root / "code" / "th_14_panels.js").read_text(encoding="utf-8") +checks = [ + ("manager has polished helper", "createButtonManagerPolishedCard" in text), + ("manager has action chip helper", "createButtonManagerActionChip" in text), + ("manager card has two-row layout marker", "按钮管理列表卡片:上信息、下操作" in text), + ("manager has search surface marker", "按钮管理搜索卡片" in text), + ("editor has hero helper", "createButtonEditorHeroCard" in text), + ("editor has field spacing helper", "addButtonEditorField" in text), + ("editor hero visible text", "按钮编辑工作台" in text), + ("editor sections improved", "常用内容默认展开,低频配置可折叠" in text), +] +failed = [name for name, ok in checks if not ok] +if failed: + print("Button manager/editor layout verification FAILED:") + for name in failed: + print(" - " + name) + sys.exit(1) +print("Button manager/editor layout verification OK")