From d7d2fec5368b7dfa627c1d2011a21e5caf15c5d1 Mon Sep 17 00:00:00 2001 From: ZHU MINCHAO <48154958+MinchaoZhu@users.noreply.github.com> Date: Wed, 4 Nov 2026 09:57:18 +0800 Subject: [PATCH] ui: extract PledgerSkin common visual components and refactor WalletWindow --- src/Decentralized/PledgerSkin.cs | 255 ++++++++++++++++++++++++++++++ src/Decentralized/WalletWindow.cs | 79 ++------- 2 files changed, 265 insertions(+), 69 deletions(-) create mode 100644 src/Decentralized/PledgerSkin.cs diff --git a/src/Decentralized/PledgerSkin.cs b/src/Decentralized/PledgerSkin.cs new file mode 100644 index 0000000..bbcfd01 --- /dev/null +++ b/src/Decentralized/PledgerSkin.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Decentralized +{ + /// + /// PLEDGER 硬件风格公共皮肤与组件: + /// - 机身:自画像素九宫格 ui_hw_shell(圆角石墨,左上受光边、右下背光边),3× 像素放大 + /// - 刻字:游戏 machinery 窗口的标题栏与分隔线设为透明;标题当机身刻字(灰色粗体、字距 18、居中) + /// - 屏幕:黑色边框 ui_hw_bezel 包一块浅色墨水屏 ui_hw_paper(#D8DAD0),左右对称居中 + /// - 文字:深墨色 Ink,次要信息灰 _dim,警示暗红 _note + /// - 按钮:黑底白字触控键 Key / KeyText;选中的项反色(白底黑框黑字加粗)加准星角标 ui_hw_sel,不用 [方括号] + /// - 数据表:列右对齐,整行可点,选中行整行反色(黑底白字) + /// - 滚动区:背景透明露出墨水屏底色,滚动条滑块为墨色 + /// + public static class PledgerSkin + { + public static readonly Color Ink = new Color(0.12f, 0.12f, 0.14f); + public static readonly Color Key = new Color(0.16f, 0.16f, 0.19f); + public static readonly Color KeyText = new Color(0.93f, 0.93f, 0.90f); + public static readonly Color Dim = new Color(0.38f, 0.39f, 0.40f); + public static readonly Color Alert = new Color(0.62f, 0.16f, 0.12f); + public static readonly Color Title = new Color(0.62f, 0.64f, 0.70f); + public static readonly Color Paper = new Color(0.847f, 0.855f, 0.816f); // #D8DAD0 + public static readonly Color RowNormal = new Color(0f, 0f, 0f, 0.04f); + public static readonly Color Transparent = new Color(0f, 0f, 0f, 0f); + public static readonly Color Separator = new Color(0.12f, 0.12f, 0.14f, 0.18f); + + private static readonly Dictionary Sliced = new Dictionary(); + + /// 把本 mod 的像素图做成九宫格贴到 Image 上:边角 border 像素不拉伸,一个像素放大成 3 个界面单位。 + public static void Slice(Image img, string key, int border) + { + var src = Items.ModSprite(key); + if (img == null || src == null) return; + if (!Sliced.TryGetValue(key, out var sp)) + { + src.texture.filterMode = FilterMode.Point; + sp = Sprite.Create(src.texture, src.rect, new Vector2(0.5f, 0.5f), src.pixelsPerUnit, 0, SpriteMeshType.FullRect, new Vector4(border, border, border, border)); + Sliced[key] = sp; + } + img.sprite = sp; + img.type = Image.Type.Sliced; + img.pixelsPerUnitMultiplier = 100f / (3f * sp.pixelsPerUnit); + img.color = Color.white; + } + + /// 屏上的选择按钮(币种、档位、子页):选中的一个画成白底黑框黑字、四角带准星角标。 + public static void Pick(CustomUIBuilder b, string text, bool selected, Action onClick, ICollection picked) + { + b.AddButton(text, onClick); + if (selected && picked != null) picked.Add(b.Last.root); + } + + /// 数据表行定义:整行高亮与点击回调。 + public sealed class RowDef + { + public GameObject Root; + public Image Image; + public bool Selected; + public Action OnClick; + } + + /// 数据表的一行:列右对齐(第一列默认左对齐),整行可点击,选中行反色。 + public static void TableRow(CustomUIBuilder b, float totalW, float height, float[] widths, TextAnchor[] aligns, string[] texts, bool selected, Action onClick, ICollection rows, ICollection clear) + { + b.BeginPanel(totalW, height, TextAnchor.MiddleLeft, 6f, 0f); + var root = b.Last.root; + var img = b.Last.image; + if (rows != null) rows.Add(new RowDef { Root = root, Image = img, Selected = selected, OnClick = onClick }); + b.BeginRow(0f, TextAnchor.MiddleLeft); + for (int i = 0; i < widths.Length; i++) + { + float w = widths[i]; + var align = (aligns != null && i < aligns.Length) ? aligns[i] : (i == 0 ? TextAnchor.MiddleLeft : TextAnchor.MiddleRight); + b.BeginPanel(w, height - 6f, align, 0f, 0f); + if (clear != null) clear.Add(b.Last.image); + b.AddLabel(i < texts.Length ? texts[i] : ""); + b.End(); + } + b.End(); + b.End(); + } + + /// 数据表表头:与表行同对齐方式,加粗,无底色。 + public static void TableHeader(CustomUIBuilder b, float totalW, float height, float[] widths, TextAnchor[] aligns, string[] headers, ICollection clear) + { + b.BeginPanel(totalW, height, TextAnchor.MiddleLeft, 6f, 0f); + if (clear != null) clear.Add(b.Last.image); + b.BeginRow(0f, TextAnchor.MiddleLeft); + for (int i = 0; i < widths.Length; i++) + { + float w = widths[i]; + var align = (aligns != null && i < aligns.Length) ? aligns[i] : (i == 0 ? TextAnchor.MiddleLeft : TextAnchor.MiddleRight); + b.BeginPanel(w, height - 6f, align, 0f, 0f); + if (clear != null) clear.Add(b.Last.image); + b.AddLabel(i < headers.Length ? "" + headers[i] + "" : ""); + b.End(); + } + b.End(); + b.End(); + } + + /// 应用整套 PLEDGER 皮肤:机身、边框、墨水屏、刻字、文字颜色、触控键、滚动条与数据表行。 + public static void Apply(CustomUIWindow win, RectTransform screen, Image bezel, Image paper, ICollection picked, ICollection clear, IEnumerable rows = null) + { + if (win?.root == null) return; + Slice(win.root.GetComponent(), "ui_hw_shell", 10); + if (bezel != null) Slice(bezel, "ui_hw_bezel", 5); + if (paper != null) Slice(paper, "ui_hw_paper", 3); + + // 标题栏和分隔线透明,标题当机身刻字 + foreach (var name in new[] { "header", "rule" }) + { + var img = win.root.transform.Find(name)?.GetComponent(); + if (img != null) img.color = Transparent; + } + if (win.frame?.titleText != null) + { + var t = win.frame.titleText; + t.color = Title; + t.fontStyle = FontStyles.Bold; + t.characterSpacing = 18f; + t.fontSize = 16f; + t.horizontalAlignment = HorizontalAlignmentOptions.Center; + } + var content = win.frame?.contentRoot?.GetComponent(); + if (content != null) content.childAlignment = TextAnchor.UpperCenter; + + if (screen == null) return; + + // 收集所有表格行 root,避免按钮样式覆盖整行 + var rowRoots = new HashSet(); + if (rows != null) + { + foreach (var r in rows) + if (r?.Root != null) rowRoots.Add(r.Root); + } + + // 屏上文字:note 暗红,dim 灰,其余深墨色 + foreach (var t in screen.GetComponentsInChildren(true)) + { + if (t.GetComponentInParent