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