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 MaskClear = new Color(0f, 0f, 0f, 0.004f); public static readonly Color Separator = new Color(0.12f, 0.12f, 0.14f, 0.18f); /// 透明化底图:若附带 Mask 组件则保留极微小 alpha(0.004),避免 Mask 模板 alpha 为 0 导致子内容被整裁。 public static void SetClear(Image img) { if (img == null) return; img.color = (img.GetComponent() != null) ? MaskClear : Transparent; } 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; } /// 把输入框切片为墨水屏凹槽:浅色底、1 像素深墨边框(1:1 像素映射)。 public static void SliceGroove(Image img) { if (img == null) return; if (!Sliced.TryGetValue("ui_hw_groove", out var sp)) { var src = Items.ModSprite("ui_hw_groove"); if (src != null) { 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(2, 2, 2, 2)); } else { var tex = new Texture2D(12, 12, TextureFormat.RGBA32, false) { filterMode = FilterMode.Point }; for (int y = 0; y < 12; y++) for (int x = 0; x < 12; x++) tex.SetPixel(x, y, (x == 0 || x == 11 || y == 0 || y == 11) ? Ink : Paper); tex.Apply(); sp = Sprite.Create(tex, new Rect(0, 0, 12, 12), new Vector2(0.5f, 0.5f), 100f, 0, SpriteMeshType.FullRect, new Vector4(2, 2, 2, 2)); } Sliced["ui_hw_groove"] = sp; } img.sprite = sp; img.type = Image.Type.Sliced; img.pixelsPerUnitMultiplier = 100f / 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) SetClear(img); } 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