diff --git a/art/sprites.py b/art/sprites.py index 1a83c01..49d4482 100644 --- a/art/sprites.py +++ b/art/sprites.py @@ -739,11 +739,20 @@ def hw_selected(): return c +def hw_groove(): + """PLEDGER 墨水屏输入框凹槽(12×12,九宫格边 2):浅色底、1 像素深墨边。""" + c = Canvas(12, 12) + c.rect(0, 0, 11, 11, 'n') + c.rect(1, 1, 10, 10, 'P') + return c + + UI_ICONS = {'ui_bank': ui_bank, 'ui_poinbase': ui_poinbase, 'ui_web3': ui_web3} UI_ICONS['ui_hw_shell'] = lambda: rounded(32, 7, 'K', 'E', 'z') UI_ICONS['ui_hw_bezel'] = lambda: rounded(16, 4, 'n') UI_ICONS['ui_hw_paper'] = lambda: rounded(12, 2, 'P') UI_ICONS['ui_hw_sel'] = hw_selected +UI_ICONS['ui_hw_groove'] = hw_groove UI_ICONS['ui_hw_ok'] = lambda: hw_button('ok') UI_ICONS['ui_hw_no'] = lambda: hw_button('no') diff --git a/src/Decentralized/ModFiles/Localization/en.csv b/src/Decentralized/ModFiles/Localization/en.csv index 47d914b..d180bf5 100644 --- a/src/Decentralized/ModFiles/Localization/en.csv +++ b/src/Decentralized/ModFiles/Localization/en.csv @@ -170,9 +170,7 @@ stable.ok,"Pegged" stable.depeg,"Depegged" stable.warn,"Warning: staking subsidy pool low" stable.spiral,"Collapsing" -aml.window,"Withdrawn in the last {0} days: {1} credits (cumulative line {2})" aml.taint,"Account taint share: {0}%" -aml.chance,"At this amount, estimated review chance: {0}%" mixer.h,Mixer mixer.ok,Status: operating normally mixer.sanctioned,Status: sanctioned ({0} more day(s)). Coins withdrawn meanwhile are flagged. diff --git a/src/Decentralized/ModFiles/Localization/zh.csv b/src/Decentralized/ModFiles/Localization/zh.csv index 415b935..c1eec0b 100644 --- a/src/Decentralized/ModFiles/Localization/zh.csv +++ b/src/Decentralized/ModFiles/Localization/zh.csv @@ -169,9 +169,7 @@ stable.ok,"锚定正常" stable.depeg,"脱锚" stable.warn,"预兆:质押补贴池告急" stable.spiral,"崩盘中" -aml.window,"近 {0} 天已提取 {1} 信用点(累计线 {2})" aml.taint,"账户污点比例:{0}%" -aml.chance,"按这个数量,预计审查概率:{0}%" mixer.h,混币器 mixer.ok,状态:运行正常 mixer.sanctioned,状态:已被制裁(还有 {0} 天),此期间提出的币视为被标记。 diff --git a/src/Decentralized/ModFiles/Textures/Items/ui_hw_groove.png b/src/Decentralized/ModFiles/Textures/Items/ui_hw_groove.png new file mode 100644 index 0000000..163ef61 Binary files /dev/null and b/src/Decentralized/ModFiles/Textures/Items/ui_hw_groove.png differ diff --git a/src/Decentralized/PledgerSkin.cs b/src/Decentralized/PledgerSkin.cs index f87347d..f853de4 100644 --- a/src/Decentralized/PledgerSkin.cs +++ b/src/Decentralized/PledgerSkin.cs @@ -27,8 +27,16 @@ namespace Decentralized 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 个界面单位。 @@ -48,6 +56,35 @@ namespace Decentralized 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) { @@ -116,7 +153,7 @@ namespace Decentralized foreach (var name in new[] { "header", "rule" }) { var img = win.root.transform.Find(name)?.GetComponent(); - if (img != null) img.color = Transparent; + if (img != null) SetClear(img); } if (win.frame?.titleText != null) { @@ -147,9 +184,11 @@ namespace Decentralized t.color = t.gameObject.name.Contains("_note") ? Alert : t.gameObject.name.Contains("_dim") ? Dim : Ink; } - // 屏上输入框 + // 屏上输入框:墨水屏凹槽样式(浅色底、深墨 1px 边、深墨文字、灰色占位符) foreach (var inp in screen.GetComponentsInChildren(true)) { + var bg = inp.GetComponent() ?? inp.targetGraphic as Image; + if (bg != null) SliceGroove(bg); if (inp.textComponent != null) inp.textComponent.color = Ink; if (inp.placeholder is TextMeshProUGUI ph) ph.color = Dim; } @@ -193,35 +232,70 @@ namespace Decentralized foreach (var scroll in screen.GetComponentsInChildren(true)) { var bg = scroll.GetComponent(); - if (bg != null) bg.color = Transparent; + if (bg != null) SetClear(bg); if (scroll.viewport != null) { var vpImg = scroll.viewport.GetComponent(); // 视口的图是 Mask 的模板:alpha 为 0 会把整个内容裁掉,只能压到几乎看不见 - if (vpImg != null) vpImg.color = new Color(0f, 0f, 0f, 0.004f); + if (vpImg != null) SetClear(vpImg); } if (scroll.verticalScrollbar != null) { var sb = scroll.verticalScrollbar; - var track = sb.GetComponent(); - if (track != null) track.color = Transparent; + var sbrt = sb.transform as RectTransform; + if (sbrt != null) sbrt.sizeDelta = new Vector2(6f, sbrt.sizeDelta.y); + foreach (var img in sb.GetComponentsInChildren(true)) + { + if (sb.handleRect != null && (img.gameObject == sb.handleRect.gameObject || img.transform.IsChildOf(sb.handleRect))) + continue; + SetClear(img); + } if (sb.handleRect != null) { + sb.handleRect.sizeDelta = new Vector2(0f, sb.handleRect.sizeDelta.y); var handleImg = sb.handleRect.GetComponent(); if (handleImg != null) { handleImg.sprite = MachineryStyle.White; + handleImg.type = Image.Type.Simple; + handleImg.color = Ink; + } + } + } + if (scroll.horizontalScrollbar != null) + { + var sb = scroll.horizontalScrollbar; + var sbrt = sb.transform as RectTransform; + if (sbrt != null) sbrt.sizeDelta = new Vector2(sbrt.sizeDelta.x, 6f); + foreach (var img in sb.GetComponentsInChildren(true)) + { + if (sb.handleRect != null && (img.gameObject == sb.handleRect.gameObject || img.transform.IsChildOf(sb.handleRect))) + continue; + SetClear(img); + } + if (sb.handleRect != null) + { + sb.handleRect.sizeDelta = new Vector2(sb.handleRect.sizeDelta.x, 0f); + var handleImg = sb.handleRect.GetComponent(); + if (handleImg != null) + { + handleImg.sprite = MachineryStyle.White; + handleImg.type = Image.Type.Simple; handleImg.color = Ink; } } } } - // 清理辅助 Panel 底图 + // 清理辅助 Panel 底图与任何残留原生灰色边框的容器 if (clear != null) { foreach (var img in clear) - if (img != null) img.color = Transparent; + if (img != null) SetClear(img); + } + foreach (var img in screen.GetComponentsInChildren(true)) + { + if (img.sprite == MachineryStyle.BevelFrame) SetClear(img); } // 数据表行:反色高亮与整行点击 @@ -241,6 +315,26 @@ namespace Decentralized if (pBtn != null && pBtn.gameObject != r.Root) continue; t.color = r.Selected ? KeyText : Ink; } + if (r.Selected) + { + // 选中行整行反色(深墨底):行内的操作按钮反转为浅色底、深墨字 + foreach (var cBtn in r.Root.GetComponentsInChildren